aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorstanislavh <stanislav.honcharov@sonarsource.com>2023-03-01 13:32:28 +0100
committersonartech <sonartech@sonarsource.com>2023-03-02 20:03:50 +0000
commit58abe872d6edb1ec16b2e78dc5d952085be64f0d (patch)
tree8c1d8cf56b9be689777b93ea4abb180351569e69 /server/sonar-web
parent019be73fb00148ee912ed827284f63c33df9b95a (diff)
downloadsonarqube-58abe872d6edb1ec16b2e78dc5d952085be64f0d.tar.gz
sonarqube-58abe872d6edb1ec16b2e78dc5d952085be64f0d.zip
SONAR-18580 add RTL for settings (part1)
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts149
-rw-r--r--server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts29
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/AdditionalCategories-test.tsx52
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/CategoryDefinitionsList-test.tsx79
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx (renamed from server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx)0
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionActions-test.tsx87
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionRenderer-test.tsx58
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx90
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-it.tsx132
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx68
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx (renamed from server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx)0
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx32
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-it.tsx191
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-test.tsx71
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsAppRenderer-test.tsx69
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearch-test.tsx154
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearchRenderer-test.tsx86
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx75
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap150
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AnalysisScope-test.tsx.snap77
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionActions-test.tsx.snap143
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap571
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap358
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap96
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-it.tsx.snap (renamed from server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap83
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsApp-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap503
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsSearchRenderer-test.tsx.snap134
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap135
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/inputs/SimpleInput.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/settings/styles.css10
-rw-r--r--server/sonar-web/src/main/js/apps/settings/utils.ts6
42 files changed, 530 insertions, 3248 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts
index ac36f1448f1..abfd964dcb8 100644
--- a/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts
+++ b/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts
@@ -19,57 +19,164 @@
*/
import { cloneDeep } from 'lodash';
import { HousekeepingPolicy } from '../../apps/audit-logs/utils';
+import { mockDefinition } from '../../helpers/mocks/settings';
import { BranchParameters } from '../../types/branch-like';
-import { SettingsKey, SettingValue } from '../../types/settings';
-import { getAllValues, getValue, getValues } from '../settings';
+import {
+ ExtendedSettingDefinition,
+ SettingDefinition,
+ SettingsKey,
+ SettingType,
+ SettingValue,
+} from '../../types/settings';
+import {
+ getAllValues,
+ getDefinitions,
+ getValue,
+ getValues,
+ resetSettingValue,
+ setSettingValue,
+} from '../settings';
+
+const isEmptyString = (i: string) => i.trim() === '';
+
+export const DEFAULT_DEFINITIONS_MOCK = [
+ mockDefinition({
+ category: 'general',
+ key: 'sonar.announcement.message',
+ subCategory: 'Announcement',
+ name: 'Announcement message',
+ description: 'Enter message',
+ defaultValue: '.js,.jsx,.cjs,.vue,.mjs',
+ type: SettingType.TEXT,
+ }),
+ mockDefinition({
+ category: 'general',
+ key: 'sonar.ce.parallelProjectTasks',
+ subCategory: 'Compute Engine',
+ name: 'Run analysis in paralel',
+ description: 'Enter message',
+ defaultValue: '.js,.jsx,.cjs,.vue,.mjs',
+ type: SettingType.TEXT,
+ }),
+ mockDefinition({
+ category: 'javascript',
+ key: 'sonar.javascript.globals',
+ subCategory: 'General',
+ name: 'Global Variables',
+ description: 'List of Global variables',
+ multiValues: true,
+ defaultValue: 'angular,google,d3',
+ }),
+ mockDefinition({
+ category: 'javascript',
+ key: 'sonar.javascript.file.suffixes',
+ subCategory: 'General',
+ name: 'JavaScript File Suffixes',
+ description: 'List of suffixes for files to analyze',
+ multiValues: true,
+ defaultValue: '.js,.jsx,.cjs,.vue,.mjs',
+ }),
+ mockDefinition({
+ category: 'External Analyzers',
+ key: 'sonar.androidLint.reportPaths',
+ subCategory: 'Android',
+ name: 'Android Lint Report Files',
+ description: 'Paths to xml files',
+ multiValues: true,
+ }),
+];
export default class SettingsServiceMock {
- settingValues: SettingValue[];
- defaultValues: SettingValue[] = [
+ #defaultValues: SettingValue[] = [
{
key: SettingsKey.AuditHouseKeeping,
value: HousekeepingPolicy.Weekly,
},
+ {
+ key: 'sonar.javascript.globals',
+ values: ['angular', 'google', 'd3'],
+ },
];
+ #settingValues: SettingValue[] = cloneDeep(this.#defaultValues);
+
+ #definitions: ExtendedSettingDefinition[] = cloneDeep(DEFAULT_DEFINITIONS_MOCK);
+
constructor() {
- this.settingValues = cloneDeep(this.defaultValues);
- (getValue as jest.Mock).mockImplementation(this.handleGetValue);
- (getValues as jest.Mock).mockImplementation(this.handleGetValues);
- (getAllValues as jest.Mock).mockImplementation(this.handleGetAllValues);
+ jest.mocked(getDefinitions).mockImplementation(this.handleGetDefinitions);
+ jest.mocked(getValue).mockImplementation(this.handleGetValue);
+ jest.mocked(getValues).mockImplementation(this.handleGetValues);
+ jest.mocked(getAllValues).mockImplementation(this.handleGetAllValues);
+ jest.mocked(setSettingValue).mockImplementation(this.handleSetSettingValue);
+ jest.mocked(resetSettingValue).mockImplementation(this.handleResetSettingValue);
}
handleGetValue = (data: { key: string; component?: string } & BranchParameters) => {
- const setting = this.settingValues.find((s) => s.key === data.key);
+ const setting = this.#settingValues.find((s) => s.key === data.key) as SettingValue;
return this.reply(setting);
};
handleGetValues = (data: { keys: string[]; component?: string } & BranchParameters) => {
- const settings = this.settingValues.filter((s) => data.keys.includes(s.key));
+ const settings = this.#settingValues.filter((s) => data.keys.includes(s.key));
return this.reply(settings);
};
handleGetAllValues = () => {
- return this.reply(this.settingValues);
+ return this.reply(this.#settingValues);
+ };
+
+ handleGetDefinitions = () => {
+ return this.reply(this.#definitions);
+ };
+
+ handleSetSettingValue = (definition: SettingDefinition, value: any): Promise<void> => {
+ if (
+ (typeof value === 'string' && isEmptyString(value)) ||
+ (value instanceof Array && value.some(isEmptyString))
+ ) {
+ throw new ResponseError('validation error', {
+ errors: [{ msg: 'A non empty value must be provided' }],
+ });
+ }
+
+ this.set(definition.key, value);
+
+ return this.reply(undefined);
+ };
+
+ handleResetSettingValue = (data: { keys: string; component?: string } & BranchParameters) => {
+ const setting = this.#settingValues.find((s) => s.key === data.keys) as SettingValue;
+ const definition = this.#definitions.find(
+ (d) => d.key === data.keys
+ ) as ExtendedSettingDefinition;
+ if (definition.multiValues === true) {
+ setting.values = definition.defaultValue?.split(',') ?? [];
+ } else {
+ setting.value = definition.defaultValue ?? '';
+ }
+
+ return this.reply(undefined);
};
emptySettings = () => {
- this.settingValues = [];
+ this.#settingValues = [];
return this;
};
- set = (key: SettingsKey, value: string) => {
- const setting = this.settingValues.find((s) => s.key === key);
+ set = (key: string | SettingsKey, value: any) => {
+ const setting = this.#settingValues.find((s) => s.key === key);
if (setting) {
setting.value = value;
+ setting.values = value;
} else {
- this.settingValues.push({ key, value });
+ this.#settingValues.push({ key, value });
}
return this;
};
reset = () => {
- this.settingValues = cloneDeep(this.defaultValues);
+ this.#settingValues = cloneDeep(this.#defaultValues);
+ this.#definitions = cloneDeep(DEFAULT_DEFINITIONS_MOCK);
return this;
};
@@ -77,3 +184,13 @@ export default class SettingsServiceMock {
return Promise.resolve(cloneDeep(response));
}
}
+
+class ResponseError extends Error {
+ #response: any;
+ constructor(name: string, response: any) {
+ super(name);
+ this.#response = response;
+ }
+
+ json = () => Promise.resolve(this.#response);
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
index fde31d3de76..c3643ff8af2 100644
--- a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
+++ b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { hasMessage } from '../../../helpers/l10n';
import { mockComponent } from '../../../helpers/mocks/component';
import { mockDefinition, mockSettingValue } from '../../../helpers/mocks/settings';
import {
@@ -25,7 +26,18 @@ import {
SettingFieldDefinition,
SettingType,
} from '../../../types/settings';
-import { buildSettingLink, getDefaultValue, getEmptyValue, getSettingValue } from '../utils';
+import {
+ buildSettingLink,
+ getDefaultValue,
+ getEmptyValue,
+ getPropertyName,
+ getSettingValue,
+} from '../utils';
+
+jest.mock('../../../helpers/l10n', () => ({
+ ...jest.requireActual('../../../helpers/l10n'),
+ hasMessage: jest.fn(),
+}));
const fields = [
{ key: 'foo', type: 'STRING' } as SettingFieldDefinition,
@@ -40,6 +52,21 @@ const settingDefinition: ExtendedSettingDefinition = {
subCategory: 'subtest',
};
+describe('getPropertyName', () => {
+ it('should return correct value for existing translation', () => {
+ jest.mocked(hasMessage).mockImplementation(() => true);
+ expect(getPropertyName(mockDefinition())).toBe('property.foo.name');
+ });
+
+ it('should return a name when translation doesn`t exist', () => {
+ jest.mocked(hasMessage).mockImplementation(() => false);
+ expect(getPropertyName(mockDefinition({ name: 'nicename', key: 'keey' }))).toBe('nicename');
+ });
+ it('should return a key when translation and name dont exist', () => {
+ expect(getPropertyName(mockDefinition({ key: 'keey' }))).toBe('keey');
+ });
+});
+
describe('#getEmptyValue()', () => {
it('should work for property sets', () => {
const setting: ExtendedSettingDefinition = {
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
index a9e7f6f89f7..a039b597626 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
@@ -38,7 +38,7 @@ export interface CategoriesListProps extends WithAvailableFeaturesProps {
selectedCategory: string;
}
-export function CategoriesList(props: CategoriesListProps) {
+function CategoriesList(props: CategoriesListProps) {
const { categories, component, defaultCategory, selectedCategory } = props;
const categoriesWithName = categories
diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx b/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx
index 22130b7493d..f90f0072f7c 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionActions.tsx
@@ -20,9 +20,9 @@
import * as React from 'react';
import { Button, ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
import Modal from '../../../components/controls/Modal';
-import { translate } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
import { Setting } from '../../../types/settings';
-import { getDefaultValue, isEmptyValue } from '../utils';
+import { getDefaultValue, getPropertyName, isEmptyValue } from '../utils';
type Props = {
changedValue?: string;
@@ -100,7 +100,14 @@ export default class DefinitionActions extends React.PureComponent<Props, State>
)}
{showReset && (
- <Button className="spacer-right" onClick={this.handleReset}>
+ <Button
+ className="spacer-right"
+ aria-label={translateWithParameters(
+ 'settings.definition.reset',
+ getPropertyName(setting.definition)
+ )}
+ onClick={this.handleReset}
+ >
{translate('reset_verb')}
</Button>
)}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx b/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx
index f9d2306f11e..e05ce398856 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/SettingsApp.tsx
@@ -40,7 +40,7 @@ interface State {
loading: boolean;
}
-export class SettingsApp extends React.PureComponent<Props, State> {
+class SettingsApp extends React.PureComponent<Props, State> {
mounted = false;
state: State = { definitions: [], loading: true };
diff --git a/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx
index 9d844cc2133..7bc291da1a8 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx
@@ -40,7 +40,7 @@ export interface SettingsAppRendererProps {
location: Location;
}
-export function SettingsAppRenderer(props: SettingsAppRendererProps) {
+function SettingsAppRenderer(props: SettingsAppRendererProps) {
const { definitions, component, loading, location } = props;
const categories = React.useMemo(() => {
@@ -92,7 +92,7 @@ export function SettingsAppRenderer(props: SettingsAppRendererProps) {
<div className="layout-page-main-inner">
{/* Adding a key to force re-rendering of the category content, so that it resets the scroll position */}
<div className="big-padded" key={selectedCategory}>
- {foundAdditionalCategory && shouldRenderAdditionalCategory ? (
+ {shouldRenderAdditionalCategory ? (
foundAdditionalCategory.renderComponent({
categories,
component,
diff --git a/server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx
index f79861d91bf..c1cfa10fca9 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx
@@ -69,7 +69,11 @@ export default function SettingsSearchRenderer(props: SettingsSearchRendererProp
/>
{showResults && (
<DropdownOverlay noPadding={true}>
- <ul className="settings-search-results menu" ref={scrollableNodeRef}>
+ <ul
+ className="settings-search-results menu"
+ title={translate('settings.search.results')}
+ ref={scrollableNodeRef}
+ >
{results && results.length > 0 ? (
results.map((r) => (
<li
diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
index b904584590f..697791cc20d 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
@@ -36,7 +36,7 @@ export interface SubCategoryDefinitionsListProps {
displaySubCategoryTitle?: boolean;
}
-export class SubCategoryDefinitionsList extends React.PureComponent<SubCategoryDefinitionsListProps> {
+class SubCategoryDefinitionsList extends React.PureComponent<SubCategoryDefinitionsListProps> {
componentDidUpdate(prevProps: SubCategoryDefinitionsListProps) {
const { hash } = this.props.location;
if (hash && prevProps.location.hash !== hash) {
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AdditionalCategories-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AdditionalCategories-test.tsx
deleted file mode 100644
index a9977f29685..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AdditionalCategories-test.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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 { find } from 'lodash';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { PULL_REQUEST_DECORATION_BINDING_CATEGORY } from '../../constants';
-import { ADDITIONAL_CATEGORIES } from '../AdditionalCategories';
-
-it('should render additional categories component correctly', () => {
- ADDITIONAL_CATEGORIES.forEach((cat) => {
- expect(
- cat.renderComponent({
- categories: [],
- component: mockComponent(),
- definitions: [],
- selectedCategory: 'TEST',
- })
- ).toMatchSnapshot();
- });
-});
-
-it('should not render pull request decoration binding component when the component is not defined', () => {
- const category = find(
- ADDITIONAL_CATEGORIES,
- (c) => c.key === PULL_REQUEST_DECORATION_BINDING_CATEGORY
- );
-
- expect(
- category!.renderComponent({
- categories: [],
- component: undefined,
- definitions: [],
- selectedCategory: '',
- })
- ).toBeUndefined();
-});
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
index e66cafe8d49..f860ebab0ab 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
@@ -22,7 +22,7 @@ import * as React from 'react';
import { mockComponent } from '../../../../helpers/mocks/component';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
import { AdditionalCategory } from '../AdditionalCategories';
-import { CategoriesList, CategoriesListProps } from '../AllCategoriesList';
+import CategoriesList, { CategoriesListProps } from '../AllCategoriesList';
jest.mock('../AdditionalCategories', () => ({
ADDITIONAL_CATEGORIES: [
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx
deleted file mode 100644
index c7ad9c69876..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 { mockComponent } from '../../../../helpers/mocks/component';
-import { AnalysisScope } from '../AnalysisScope';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
- return shallow(
- <AnalysisScope
- categories={[]}
- component={mockComponent()}
- definitions={[]}
- selectedCategory="TEST"
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/CategoryDefinitionsList-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/CategoryDefinitionsList-test.tsx
deleted file mode 100644
index 926c874e2b6..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/CategoryDefinitionsList-test.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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 { getValues } from '../../../../api/settings';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockDefinition, mockSettingValue } from '../../../../helpers/mocks/settings';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import CategoryDefinitionsList from '../CategoryDefinitionsList';
-
-jest.mock('../../../../api/settings', () => ({
- getValues: jest.fn().mockResolvedValue([]),
-}));
-
-it('should load settings values', async () => {
- const settings = [mockSettingValue({ key: 'yes' }), mockSettingValue({ key: 'yesagain' })];
- (getValues as jest.Mock).mockResolvedValueOnce(settings);
-
- const definitions = [
- mockDefinition({ category: 'general', key: 'yes' }),
- mockDefinition({ category: 'other', key: 'nope' }),
- mockDefinition({ category: 'general', key: 'yesagain' }),
- ];
-
- const wrapper = shallowRender({
- definitions,
- });
-
- await waitAndUpdate(wrapper);
-
- expect(getValues).toHaveBeenCalledWith({ keys: ['yes', 'yesagain'], component: undefined });
-
- expect(wrapper.state().settings).toEqual([
- { definition: definitions[0], settingValue: settings[0] },
- { definition: definitions[2], settingValue: settings[1] },
- ]);
-});
-
-it('should reload on category change', async () => {
- const definitions = [
- mockDefinition({ category: 'general', key: 'yes' }),
- mockDefinition({ category: 'other', key: 'nope' }),
- mockDefinition({ category: 'general', key: 'yesagain' }),
- ];
- const wrapper = shallowRender({ component: mockComponent({ key: 'comp-key' }), definitions });
-
- await waitAndUpdate(wrapper);
-
- expect(getValues).toHaveBeenCalledWith({ keys: ['yes', 'yesagain'], component: 'comp-key' });
-
- wrapper.setProps({ category: 'other' });
-
- await waitAndUpdate(wrapper);
-
- expect(getValues).toHaveBeenCalledWith({ keys: ['nope'], component: 'comp-key' });
-});
-
-function shallowRender(props: Partial<CategoryDefinitionsList['props']> = {}) {
- return shallow<CategoryDefinitionsList>(
- <CategoryDefinitionsList category="general" definitions={[]} {...props} />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx
index 40869c50470..40869c50470 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionActions-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionActions-test.tsx
deleted file mode 100644
index d542b9a7030..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionActions-test.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 { ExtendedSettingDefinition, SettingType } from '../../../../types/settings';
-import DefinitionActions from '../DefinitionActions';
-
-const definition: ExtendedSettingDefinition = {
- category: 'baz',
- description: 'lorem',
- fields: [],
- key: 'key',
- name: 'foobar',
- options: [],
- subCategory: 'bar',
- type: SettingType.STRING,
-};
-
-const settings = {
- key: 'key',
- hasValue: true,
- definition,
- value: 'baz',
-};
-
-it('displays default message when value is default', () => {
- const wrapper = shallowRender('', false, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays save button when it can be saved', () => {
- const wrapper = shallowRender('foo', false, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays cancel button when value changed and no error', () => {
- const wrapper = shallowRender('foo', false, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays cancel button when value changed and has error', () => {
- const wrapper = shallowRender('foo', true, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('disables save button on error', () => {
- const wrapper = shallowRender('foo', true, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays reset button when empty and not default', () => {
- const wrapper = shallowRender('', true, false);
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(changedValue: string, hasError: boolean, isDefault: boolean) {
- return shallow(
- <DefinitionActions
- isEditing={false}
- changedValue={changedValue}
- hasError={hasError}
- hasValueChanged={changedValue !== ''}
- isDefault={isDefault}
- onCancel={jest.fn()}
- onReset={jest.fn()}
- onSave={jest.fn()}
- setting={settings}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionRenderer-test.tsx
deleted file mode 100644
index 0010f95432d..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionRenderer-test.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 { mockDefinition, mockSettingValue } from '../../../../helpers/mocks/settings';
-import DefinitionRenderer, { DefinitionRendererProps } from '../DefinitionRenderer';
-
-it('should render correctly', () => {
- expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
- expect(
- shallowRender({ definition: mockDefinition({ description: 'description' }) })
- ).toMatchSnapshot('with description');
- expect(
- shallowRender({
- validationMessage: 'validation message',
- })
- ).toMatchSnapshot('in error');
- expect(shallowRender({ success: true })).toMatchSnapshot('success');
- expect(
- shallowRender({ settingValue: mockSettingValue({ key: 'foo', value: 'original value' }) })
- ).toMatchSnapshot('original value');
-
- expect(shallowRender({ changedValue: 'new value' })).toMatchSnapshot('changed value');
-});
-
-function shallowRender(props: Partial<DefinitionRendererProps> = {}) {
- return shallow<DefinitionRendererProps>(
- <DefinitionRenderer
- isEditing={false}
- definition={mockDefinition()}
- loading={false}
- onCancel={jest.fn()}
- onChange={jest.fn()}
- onEditing={jest.fn()}
- onReset={jest.fn()}
- onSave={jest.fn()}
- success={false}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx
deleted file mode 100644
index c0ccbc0a228..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 { sendTestEmail } from '../../../../api/settings';
-import { mockLoggedInUser } from '../../../../helpers/testMocks';
-import { change, submit, waitAndUpdate } from '../../../../helpers/testUtils';
-import { EmailForm } from '../EmailForm';
-
-jest.mock('../../../../helpers/request', () => ({
- parseError: jest.fn().mockResolvedValue('Error message'),
-}));
-
-jest.mock('../../../../api/settings', () => ({
- sendTestEmail: jest.fn().mockResolvedValue(null),
-}));
-
-it('should render correctly', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot('default');
- wrapper.setState({ loading: true });
- expect(wrapper).toMatchSnapshot('sending');
- wrapper.setState({ loading: false, success: 'email@example.com' });
- expect(wrapper).toMatchSnapshot('success');
- wrapper.setState({ success: undefined, error: 'Some error message' });
- expect(wrapper).toMatchSnapshot('error');
-});
-
-it('should correctly control the inputs', () => {
- const wrapper = shallowRender();
-
- change(wrapper.find('#test-email-to'), 'new@recipient.com');
- expect(wrapper.state().recipient).toBe('new@recipient.com');
-
- change(wrapper.find('#test-email-subject'), 'New subject');
- expect(wrapper.state().subject).toBe('New subject');
-
- change(wrapper.find('#test-email-message'), 'New message');
- expect(wrapper.state().message).toBe('New message');
-});
-
-it('should correctly test the email sending', async () => {
- const wrapper = shallowRender();
-
- submit(wrapper.find('form'));
- expect(sendTestEmail).toHaveBeenCalledWith(
- 'luke@skywalker.biz',
- 'email_configuration.test.subject',
- 'email_configuration.test.message_text'
- );
- expect(wrapper.state().loading).toBe(true);
-
- await waitAndUpdate(wrapper);
-
- expect(wrapper.state().loading).toBe(false);
- expect(wrapper.state().error).toBeUndefined();
- expect(wrapper.state().success).toBe('luke@skywalker.biz');
-
- (sendTestEmail as jest.Mock).mockRejectedValueOnce(null);
-
- submit(wrapper.find('form'));
-
- await waitAndUpdate(wrapper);
-
- expect(wrapper.state().success).toBeUndefined();
- expect(wrapper.state().error).toBe('Error message');
-});
-
-function shallowRender(props: Partial<EmailForm['props']> = {}) {
- return shallow<EmailForm>(
- <EmailForm currentUser={mockLoggedInUser({ email: 'luke@skywalker.biz' })} {...props} />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-it.tsx
new file mode 100644
index 00000000000..9e0868ebbf4
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-it.tsx
@@ -0,0 +1,132 @@
+/*
+ * 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 { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { last } from 'lodash';
+import React from 'react';
+import { byRole, byText } from 'testing-library-selector';
+import SettingsServiceMock, {
+ DEFAULT_DEFINITIONS_MOCK,
+} from '../../../../api/mocks/SettingsServiceMock';
+import { mockComponent } from '../../../../helpers/mocks/component';
+import { renderApp } from '../../../../helpers/testReactTestingUtils';
+import { AdditionalCategoryComponentProps } from '../AdditionalCategories';
+import Languages from '../Languages';
+
+jest.mock('../../../../api/settings');
+
+let settingsMock: SettingsServiceMock;
+
+beforeAll(() => {
+ settingsMock = new SettingsServiceMock();
+});
+
+afterEach(() => {
+ settingsMock.reset();
+});
+
+beforeEach(jest.clearAllMocks);
+
+const ui = {
+ languagesHeading: byRole('heading', { name: 'property.category.languages' }),
+ languagesSelect: byRole('combobox', { name: 'property.category.languages' }),
+ jsGeneralSubCategoryHeading: byRole('heading', { name: 'property.category.javascript.General' }),
+ jsGlobalVariablesHeading: byRole('heading', {
+ name: 'property.sonar.javascript.globals.name',
+ }),
+ jsGlobalVariablesDescription: byText('List of Global variables'),
+ jsFileSuffixesHeading: byRole('heading', {
+ name: 'property.sonar.javascript.file.suffixes.name',
+ }),
+ jsGlobalVariablesInput: byRole('textbox', { name: 'property.sonar.javascript.globals.name' }),
+ jsResetGlobalVariablesButton: byRole('button', {
+ name: 'settings.definition.reset.property.sonar.javascript.globals.name',
+ }),
+
+ validationMsg: byText('settings.state.validation_failed.A non empty value must be provided'),
+ saveButton: byRole('button', { name: 'save' }),
+ cancelButton: byRole('button', { name: 'cancel' }),
+ resetButton: byRole('button', { name: 'reset_verb' }),
+};
+
+it('renders Language with selected Javascript category', async () => {
+ renderLanguages({ selectedCategory: 'javascript' });
+
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ expect(await ui.jsGeneralSubCategoryHeading.find()).toBeInTheDocument();
+ expect(ui.jsGlobalVariablesHeading.get()).toBeInTheDocument();
+ expect(ui.jsFileSuffixesHeading.get()).toBeInTheDocument();
+});
+
+it('render Language without definitions', async () => {
+ renderLanguages({ selectedCategory: 'niceLanguage' });
+
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ expect(screen.queryByText(/niceLanguage/)).not.toBeInTheDocument();
+});
+
+it('can save/reset/cancel or see error for custom mocked multi values definition Global Variables', async () => {
+ const user = userEvent.setup();
+ renderLanguages({ selectedCategory: 'javascript' });
+
+ const jsVarsInputs = await ui.jsGlobalVariablesInput.findAll();
+ const lastInput = last(jsVarsInputs);
+ // Adding new js variable (multi-values input)
+ expect(jsVarsInputs).toHaveLength(4);
+
+ // Should see a validation message on typing empty string
+ await user.type(lastInput as HTMLElement, ' ');
+ await user.click(await ui.saveButton.find());
+ expect(await ui.validationMsg.find()).toBeInTheDocument();
+
+ // Should save variable
+ await user.type(lastInput as HTMLElement, 'Testing');
+ await user.click(await ui.saveButton.find());
+ expect(ui.validationMsg.query()).not.toBeInTheDocument();
+ expect(lastInput).toHaveValue(' Testing');
+
+ // Should reset to previous state on clicking cancel
+ await user.type(last(ui.jsGlobalVariablesInput.getAll()) as HTMLElement, 'Testing2');
+ await user.click(ui.cancelButton.get());
+ expect(last(ui.jsGlobalVariablesInput.getAll())).not.toHaveValue('Testing2');
+
+ // Clicking reset opens dialog and reset to default on confirm
+ const defaultValues = ['angular', 'google', 'd3', ''];
+ await user.click(ui.jsResetGlobalVariablesButton.get());
+ await user.click(ui.resetButton.get());
+ const newInputs = ui.jsGlobalVariablesInput.getAll();
+ defaultValues.forEach((value, index) => expect(newInputs[index]).toHaveValue(value));
+});
+
+function renderLanguages(
+ overrides: Partial<AdditionalCategoryComponentProps> = {},
+ component = mockComponent()
+) {
+ return renderApp(
+ '/',
+ <Languages
+ definitions={DEFAULT_DEFINITIONS_MOCK}
+ component={component}
+ categories={['javascript', 'sjava']}
+ selectedCategory=""
+ {...overrides}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx
deleted file mode 100644
index 2a54262416e..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 Select from '../../../../components/controls/Select';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockLocation, mockRouter } from '../../../../helpers/testMocks';
-import CategoryDefinitionsList from '../CategoryDefinitionsList';
-import { Languages, LanguagesProps } from '../Languages';
-
-it('should render correctly', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render correctly with an unknow language', () => {
- const wrapper = shallowRender({ selectedCategory: 'unknown' });
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should correctly handle a change of the selected language', () => {
- const push = jest.fn();
- const router = mockRouter({ push });
- const wrapper = shallowRender({ router });
- expect(wrapper.find(CategoryDefinitionsList).props().category).toBe('java');
-
- const { onChange } = wrapper.find(Select).props();
-
- onChange({ label: '', originalValue: 'CoBoL', value: 'cobol' });
- expect(push).toHaveBeenCalledWith(expect.objectContaining({ query: { category: 'CoBoL' } }));
-});
-
-it('should correctly show the subcategory for a component', () => {
- const component = mockComponent();
- const wrapper = shallowRender({ component });
- expect(wrapper.find(CategoryDefinitionsList).props().component).toBe(component);
-});
-
-function shallowRender(props: Partial<LanguagesProps> = {}) {
- return shallow(
- <Languages
- categories={['Java', 'JavaScript', 'COBOL']}
- component={undefined}
- definitions={[]}
- location={mockLocation()}
- router={mockRouter()}
- selectedCategory="java"
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx
index c95f2e18bb8..c95f2e18bb8 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx
deleted file mode 100644
index 41843017237..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 { mockComponent } from '../../../../helpers/mocks/component';
-import PageHeader, { PageHeaderProps } from '../PageHeader';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('global');
- expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('for project');
-});
-
-function shallowRender(props: Partial<PageHeaderProps> = {}) {
- return shallow<PageHeaderProps>(<PageHeader definitions={[]} {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-it.tsx
new file mode 100644
index 00000000000..c4a7d2582f7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-it.tsx
@@ -0,0 +1,191 @@
+/*
+ * 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 { within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import React from 'react';
+import { Route } from 'react-router-dom';
+import selectEvent from 'react-select-event';
+import { byRole } from 'testing-library-selector';
+import SettingsServiceMock from '../../../../api/mocks/SettingsServiceMock';
+import { KeyboardKeys } from '../../../../helpers/keycodes';
+import { mockComponent } from '../../../../helpers/mocks/component';
+import {
+ renderAppRoutes,
+ renderAppWithComponentContext,
+ RenderContext,
+} from '../../../../helpers/testReactTestingUtils';
+import { Feature } from '../../../../types/features';
+import { Component } from '../../../../types/types';
+import routes from '../../routes';
+
+jest.mock('../../../../api/settings');
+
+let settingsMock: SettingsServiceMock;
+
+beforeAll(() => {
+ settingsMock = new SettingsServiceMock();
+});
+
+afterEach(() => {
+ settingsMock.reset();
+});
+
+beforeEach(jest.clearAllMocks);
+
+const ui = {
+ categoryLink: (category: string) => byRole('link', { name: category }),
+ announcementHeading: byRole('heading', { name: 'property.category.general.Announcement' }),
+
+ languagesHeading: byRole('heading', { name: 'property.category.languages' }),
+ languagesSelect: byRole('combobox', { name: 'property.category.languages' }),
+ jsGeneralSubCategoryHeading: byRole('heading', { name: 'property.category.javascript.General' }),
+
+ settingsSearchInput: byRole('searchbox', { name: 'settings.search.placeholder' }),
+ searchList: byRole('list', { name: 'settings.search.results' }),
+ searchItem: (key: string) => byRole('link', { name: new RegExp(key) }),
+ searchClear: byRole('button', { name: 'clear' }),
+
+ externalAnalyzersAndroidHeading: byRole('heading', {
+ name: 'property.category.External Analyzers.Android',
+ }),
+ generalComputeEngineHeading: byRole('heading', {
+ name: 'property.category.general.Compute Engine',
+ }),
+};
+
+describe('Global Settings', () => {
+ it('renders categories list and definitions', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp();
+
+ const globalCategories = [
+ 'property.category.general',
+ 'property.category.languages',
+ 'property.category.External Analyzers',
+ 'settings.new_code_period.category',
+ 'property.category.almintegration',
+ ];
+
+ expect(await ui.categoryLink(globalCategories[0]).find()).toBeInTheDocument();
+ globalCategories.forEach((name) => {
+ expect(ui.categoryLink(name).get()).toBeInTheDocument();
+ });
+
+ expect(await ui.announcementHeading.find()).toBeInTheDocument();
+
+ // Navigating to Languages category
+ await user.click(await ui.categoryLink('property.category.languages').find());
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ });
+
+ it('renders Language category and can select any language', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp();
+
+ // Navigating to Languages category
+ await user.click(await ui.categoryLink('property.category.languages').find());
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+
+ await selectEvent.select(ui.languagesSelect.get(), 'property.category.javascript');
+ expect(await ui.jsGeneralSubCategoryHeading.find()).toBeInTheDocument();
+ });
+
+ it('can search definitions by name or key', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp();
+
+ expect(await ui.settingsSearchInput.find()).toBeInTheDocument();
+
+ // List popup should be closed if input is empty
+ await user.click(ui.settingsSearchInput.get());
+ expect(ui.searchList.query()).not.toBeInTheDocument();
+
+ // Should shot 'no results' based on input value
+ await user.type(ui.settingsSearchInput.get(), 'asdjasnd');
+ expect(ui.searchList.get()).toBeInTheDocument();
+ expect(within(ui.searchList.get()).getByText('no_results')).toBeInTheDocument();
+ await user.click(ui.searchClear.get());
+
+ // Should show results based on input value
+ const searchResultsKeys = [
+ 'sonar.announcement.message',
+ 'sonar.ce.parallelProjectTasks',
+ 'sonar.androidLint.reportPaths',
+ ];
+
+ await user.type(ui.settingsSearchInput.get(), 'an');
+ searchResultsKeys.forEach((key) => expect(ui.searchItem(key).get()).toBeInTheDocument());
+ expect(ui.searchItem('sonar.javascript.globals').query()).not.toBeInTheDocument();
+
+ // Navigating through keyboard
+ await user.keyboard(`{${KeyboardKeys.DownArrow}}`);
+ await user.keyboard(`{${KeyboardKeys.UpArrow}}`);
+ await user.keyboard(`{${KeyboardKeys.DownArrow}}`);
+ await user.keyboard(`{${KeyboardKeys.Enter}}`);
+
+ expect(await ui.externalAnalyzersAndroidHeading.find()).toBeInTheDocument();
+
+ // Navigating through link
+ await user.click(ui.searchClear.get());
+ await user.type(ui.settingsSearchInput.get(), 'an');
+ await user.click(ui.searchItem(searchResultsKeys[1]).get());
+
+ expect(await ui.generalComputeEngineHeading.find()).toBeInTheDocument();
+ });
+});
+
+describe('Project Settings', () => {
+ it('renders categories list and definitions', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp(mockComponent(), { featureList: [Feature.BranchSupport] });
+
+ const projectCategories = [
+ 'property.category.general',
+ 'property.category.languages',
+ 'property.category.External Analyzers',
+ 'settings.pr_decoration.binding.category',
+ ];
+
+ expect(await ui.categoryLink(projectCategories[0]).find()).toBeInTheDocument();
+ projectCategories.forEach((name) => {
+ expect(ui.categoryLink(name).get()).toBeInTheDocument();
+ });
+
+ expect(await ui.announcementHeading.find()).toBeInTheDocument();
+
+ // Navigating to Languages category
+ await user.click(await ui.categoryLink('property.category.languages').find());
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ });
+});
+
+function renderSettingsApp(component?: Component, context: RenderContext = {}) {
+ const path = component ? 'project' : 'admin';
+ const wrapperRoutes = () => <Route path={path}>{routes()}</Route>;
+ const params: [string, typeof routes, RenderContext] = [
+ `${path}/settings`,
+ wrapperRoutes,
+ context,
+ ];
+
+ return component
+ ? renderAppWithComponentContext(...params, { component })
+ : renderAppRoutes(...params);
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-test.tsx
deleted file mode 100644
index a4677e66bd5..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-test.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 { getDefinitions } from '../../../../api/settings';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import {
- addSideBarClass,
- addWhitePageClass,
- removeSideBarClass,
- removeWhitePageClass,
-} from '../../../../helpers/pages';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { SettingsApp } from '../SettingsApp';
-
-jest.mock('../../../../helpers/pages', () => ({
- addSideBarClass: jest.fn(),
- addWhitePageClass: jest.fn(),
- removeSideBarClass: jest.fn(),
- removeWhitePageClass: jest.fn(),
-}));
-
-jest.mock('../../../../api/settings', () => ({
- getDefinitions: jest.fn().mockResolvedValue([]),
-}));
-
-it('should render default view correctly', async () => {
- const wrapper = shallowRender();
-
- expect(addSideBarClass).toHaveBeenCalled();
- expect(addWhitePageClass).toHaveBeenCalled();
-
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-
- expect(getDefinitions).toHaveBeenCalledWith(undefined);
-
- wrapper.unmount();
-
- expect(removeSideBarClass).toHaveBeenCalled();
- expect(removeWhitePageClass).toHaveBeenCalled();
-});
-
-it('should fetch definitions for component', async () => {
- const key = 'component-key';
- const wrapper = shallowRender({ component: mockComponent({ key }) });
-
- await waitAndUpdate(wrapper);
- expect(getDefinitions).toHaveBeenCalledWith(key);
-});
-
-function shallowRender(props: Partial<SettingsApp['props']> = {}) {
- return shallow(<SettingsApp {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsAppRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsAppRenderer-test.tsx
deleted file mode 100644
index e88fe230b01..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsAppRenderer-test.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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 ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper';
-import { mockDefinition } from '../../../../helpers/mocks/settings';
-import { mockLocation } from '../../../../helpers/testMocks';
-import {
- ALM_INTEGRATION_CATEGORY,
- ANALYSIS_SCOPE_CATEGORY,
- LANGUAGES_CATEGORY,
- NEW_CODE_PERIOD_CATEGORY,
- PULL_REQUEST_DECORATION_BINDING_CATEGORY,
-} from '../../constants';
-import { SettingsAppRenderer, SettingsAppRendererProps } from '../SettingsAppRenderer';
-
-it('should render loading correctly', () => {
- expect(shallowRender({ loading: true }).type()).toBeNull();
-});
-
-it('should render default view correctly', () => {
- const wrapper = shallowRender();
-
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find(ScreenPositionHelper).dive()).toMatchSnapshot('All Categories List');
-});
-
-it.each([
- [NEW_CODE_PERIOD_CATEGORY],
- [LANGUAGES_CATEGORY],
- [ANALYSIS_SCOPE_CATEGORY],
- [ALM_INTEGRATION_CATEGORY],
- [PULL_REQUEST_DECORATION_BINDING_CATEGORY],
-])('should render %s correctly', (category) => {
- const wrapper = shallowRender({
- location: mockLocation({ query: { category } }),
- });
-
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<SettingsAppRendererProps> = {}) {
- const definitions = [mockDefinition(), mockDefinition({ key: 'bar', category: 'general' })];
- return shallow(
- <SettingsAppRenderer
- definitions={definitions}
- loading={false}
- location={mockLocation()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearch-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearch-test.tsx
deleted file mode 100644
index bc6705d11fe..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearch-test.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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 { KeyboardKeys } from '../../../../helpers/keycodes';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockDefinition } from '../../../../helpers/mocks/settings';
-import { mockRouter } from '../../../../helpers/testMocks';
-import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils';
-import { queryToSearch } from '../../../../helpers/urls';
-import { SettingsSearch } from '../SettingsSearch';
-
-jest.mock('lunr', () =>
- jest.fn(() => ({
- search: jest.fn(() => [
- {
- ref: 'foo',
- },
- {
- ref: 'sonar.new_code_period',
- },
- ]),
- }))
-);
-
-describe('instance', () => {
- const router = mockRouter();
- const wrapper = shallowRender({ router });
-
- it('should build the index', () => {
- expect(wrapper.instance().index).not.toBeNull();
-
- const def = mockDefinition();
- expect(wrapper.instance().definitionsByKey).toEqual(
- expect.objectContaining({ [def.key]: def })
- );
- });
-
- it('should handle search', async () => {
- wrapper.instance().handleSearchChange('query');
-
- await waitAndUpdate(wrapper);
- expect(wrapper.state().searchQuery).toBe('query');
-
- expect(wrapper.instance().index.search).toHaveBeenCalled();
- expect(wrapper.state().showResults).toBe(true);
- expect(wrapper.state().results).toHaveLength(2);
- });
-
- it('should handle empty search', async () => {
- wrapper.instance().handleSearchChange('');
-
- await waitAndUpdate(wrapper);
- expect(wrapper.state().searchQuery).toBe('');
-
- expect(wrapper.instance().index.search).toHaveBeenCalled();
- expect(wrapper.state().showResults).toBe(false);
- });
-
- it('should hide results', () => {
- wrapper.setState({ showResults: true });
- wrapper.instance().hideResults();
- expect(wrapper.state().showResults).toBe(false);
- wrapper.instance().hideResults();
- });
-
- it('should handle focus', () => {
- wrapper.setState({ searchQuery: 'hi', showResults: false });
- wrapper.instance().handleFocus();
- expect(wrapper.state().showResults).toBe(true);
-
- wrapper.setState({ searchQuery: '', showResults: false });
- wrapper.instance().handleFocus();
- expect(wrapper.state().showResults).toBe(false);
- });
-
- it('should handle mouseover', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleMouseOverResult('selection');
- expect(wrapper.state().selectedResult).toBe('selection');
- });
-
- it('should handle "enter" keyboard event', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.Enter } }));
- expect(router.push).not.toHaveBeenCalled();
-
- wrapper.setState({ selectedResult: 'foo' });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.Enter } }));
-
- expect(router.push).toHaveBeenCalledWith({
- hash: '#foo',
- pathname: '/admin/settings',
- search: queryToSearch({ category: 'foo category' }),
- });
- });
-
- it('should handle "down" keyboard event', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.DownArrow } }));
- expect(wrapper.state().selectedResult).toBeUndefined();
-
- wrapper.setState({ selectedResult: 'foo' });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.DownArrow } }));
- expect(wrapper.state().selectedResult).toBe('sonar.new_code_period');
-
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.DownArrow } }));
- expect(wrapper.state().selectedResult).toBe('sonar.new_code_period');
- });
-
- it('should handle "up" keyboard event', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.UpArrow } }));
- expect(wrapper.state().selectedResult).toBeUndefined();
-
- wrapper.setState({ selectedResult: 'sonar.new_code_period' });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.UpArrow } }));
- expect(wrapper.state().selectedResult).toBe('foo');
-
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.UpArrow } }));
- expect(wrapper.state().selectedResult).toBe('foo');
- });
-});
-
-describe('project settings search', () => {
- it('should load the correct definitions', () => {
- const wrapper = shallowRender({ component: mockComponent(), definitions: [] });
-
- expect(Object.keys(wrapper.instance().definitionsByKey)).toHaveLength(1);
- });
-});
-
-function shallowRender(overrides: Partial<SettingsSearch['props']> = {}) {
- return shallow<SettingsSearch>(
- <SettingsSearch definitions={[mockDefinition()]} router={mockRouter()} {...overrides} />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearchRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearchRenderer-test.tsx
deleted file mode 100644
index 184dea62b53..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearchRenderer-test.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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 { mockDefinition } from '../../../../helpers/mocks/settings';
-import { scrollToElement } from '../../../../helpers/scrolling';
-import SettingsSearchRenderer, { SettingsSearchRendererProps } from '../SettingsSearchRenderer';
-
-jest.mock('../../../../helpers/scrolling', () => ({
- scrollToElement: jest.fn(),
-}));
-
-jest.mock('react', () => {
- return {
- ...jest.requireActual('react'),
- useRef: jest.fn(),
- useEffect: jest.fn(),
- };
-});
-
-afterAll(() => {
- jest.clearAllMocks();
-});
-
-it('should render correctly when closed', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render correctly when open', () => {
- expect(shallowRender({ showResults: true })).toMatchSnapshot('no results');
- expect(
- shallowRender({
- results: [mockDefinition({ name: 'Foo!' }), mockDefinition({ key: 'bar' })],
- selectedResult: 'bar',
- showResults: true,
- })
- ).toMatchSnapshot('results');
-});
-
-it('should scroll to selected element', () => {
- const scrollable = {};
- const scrollableRef = { current: scrollable };
- const selected = {};
- const selectedRef = { current: selected };
-
- (React.useRef as jest.Mock)
- .mockImplementationOnce(() => scrollableRef)
- .mockImplementationOnce(() => selectedRef);
- (React.useEffect as jest.Mock).mockImplementationOnce((f) => f());
-
- shallowRender();
-
- expect(scrollToElement).toHaveBeenCalled();
-});
-
-function shallowRender(overrides: Partial<SettingsSearchRendererProps> = {}) {
- return shallow<SettingsSearchRendererProps>(
- <SettingsSearchRenderer
- searchQuery=""
- showResults={false}
- onClickOutside={jest.fn()}
- onMouseOverResult={jest.fn()}
- onSearchInputChange={jest.fn()}
- onSearchInputFocus={jest.fn()}
- onSearchInputKeyDown={jest.fn()}
- {...overrides}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx
deleted file mode 100644
index f71fec69b3a..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 { mount, shallow } from 'enzyme';
-import * as React from 'react';
-import { mockSettingWithCategory } from '../../../../helpers/mocks/settings';
-import { mockLocation } from '../../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import {
- SubCategoryDefinitionsList,
- SubCategoryDefinitionsListProps,
-} from '../SubCategoryDefinitionsList';
-
-jest.mock('../../../../helpers/scrolling', () => ({
- scrollToElement: jest.fn(),
-}));
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('');
- expect(shallowRender({ subCategory: 'qg' })).toMatchSnapshot('subcategory');
-});
-
-it('should scroll if hash is defined and updated', async () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn();
- const wrapper = shallowRender({ location: mockLocation({ hash: '#qg' }) });
-
- await waitAndUpdate(wrapper);
-
- wrapper.find('h2').forEach((node) => mount(node.getElement()));
-
- expect(window.HTMLElement.prototype.scrollIntoView).toHaveBeenCalled();
-
- wrapper.setProps({ location: mockLocation({ hash: '#email' }) });
-
- expect(window.HTMLElement.prototype.scrollIntoView).toHaveBeenCalled();
-});
-
-function shallowRender(props: Partial<SubCategoryDefinitionsListProps> = {}) {
- return shallow<SubCategoryDefinitionsListProps>(
- <SubCategoryDefinitionsList
- category="general"
- location={mockLocation()}
- settings={[
- mockSettingWithCategory(),
- mockSettingWithCategory({
- definition: {
- key: 'qg',
- category: 'general',
- subCategory: 'qg',
- fields: [],
- options: [],
- description: 'awesome description',
- },
- }),
- ]}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap
deleted file mode 100644
index 1458c61e3d4..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap
+++ /dev/null
@@ -1,150 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render additional categories component correctly 1`] = `
-<withRouter(Languages)
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
-
-exports[`should render additional categories component correctly 2`] = `<NewCodePeriod />`;
-
-exports[`should render additional categories component correctly 3`] = `
-<AnalysisScope
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
-
-exports[`should render additional categories component correctly 4`] = `
-<withRouter(withAvailableFeaturesContext(AlmIntegration))
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
-
-exports[`should render additional categories component correctly 5`] = `
-<withCurrentUserContext(PRDecorationBinding)
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
-/>
-`;
-
-exports[`should render additional categories component correctly 6`] = `
-<withAvailableFeaturesContext(Authentication)
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AnalysisScope-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AnalysisScope-test.tsx.snap
deleted file mode 100644
index 1a40aa6ea1d..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AnalysisScope-test.tsx.snap
+++ /dev/null
@@ -1,77 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
- <p
- className="spacer-bottom"
- >
- settings.analysis_scope.wildcards.introduction
- <withAppStateContext(DocLink)
- className="spacer-left"
- to="/project-administration/narrowing-the-focus/"
- >
- learn_more
- </withAppStateContext(DocLink)>
- </p>
- <table
- className="data spacer-bottom"
- >
- <tbody>
- <tr>
- <td>
- *
- </td>
- <td>
- settings.analysis_scope.wildcards.zero_more_char
- </td>
- </tr>
- <tr>
- <td>
- **
- </td>
- <td>
- settings.analysis_scope.wildcards.zero_more_dir
- </td>
- </tr>
- <tr>
- <td>
- ?
- </td>
- <td>
- settings.analysis_scope.wildcards.single_char
- </td>
- </tr>
- </tbody>
- </table>
- <div
- className="settings-sub-category"
- >
- <CategoryDefinitionsList
- category="TEST"
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- />
- </div>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionActions-test.tsx.snap
deleted file mode 100644
index dddb190a866..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionActions-test.tsx.snap
+++ /dev/null
@@ -1,143 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`disables save button on error 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={true}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
-
-exports[`displays cancel button when value changed and has error 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={true}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
-
-exports[`displays cancel button when value changed and no error 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={false}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
-
-exports[`displays default message when value is default 1`] = `
-<Fragment>
- <div
- className="spacer-top note"
- style={
- {
- "lineHeight": "24px",
- }
- }
- >
- settings._default
- </div>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right"
- onClick={[Function]}
- >
- reset_verb
- </Button>
- <span
- className="note"
- >
- default
- :
- settings.default.no_value
- </span>
- </div>
-</Fragment>
-`;
-
-exports[`displays reset button when empty and not default 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right"
- onClick={[Function]}
- >
- reset_verb
- </Button>
- <span
- className="note"
- >
- default
- :
- settings.default.no_value
- </span>
- </div>
-</Fragment>
-`;
-
-exports[`displays save button when it can be saved 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={false}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap
deleted file mode 100644
index 021e807d1af..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap
+++ /dev/null
@@ -1,571 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: changed value 1`] = `
-<div
- className="settings-definition settings-definition-changed"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- />
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={true}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- value="new value"
- />
- <DefinitionActions
- changedValue="new value"
- hasError={false}
- hasValueChanged={true}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: in error 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- >
- <span
- className="text-danger"
- >
- <AlertErrorIcon
- className="spacer-right"
- />
- <span>
- settings.state.validation_failed.validation message
- </span>
- </span>
- </div>
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={true}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: loading 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- >
- <span
- className="text-info"
- >
- <i
- className="spinner spacer-right"
- />
- settings.state.saving
- </span>
- </div>
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: original value 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- />
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": true,
- "key": "foo",
- "value": "original value",
- }
- }
- value="original value"
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": true,
- "key": "foo",
- "value": "original value",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: success 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- >
- <span
- className="text-success"
- >
- <AlertSuccessIcon
- className="spacer-right"
- />
- settings.state.saved
- </span>
- </div>
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: with description 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- />
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "description": "description",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "description": "description",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap
deleted file mode 100644
index a27318bce98..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap
+++ /dev/null
@@ -1,358 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={false}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={false}
- >
- email_configuration.test.send
- </SubmitButton>
- </form>
-</div>
-`;
-
-exports[`should render correctly: error 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <div
- className="form-field"
- >
- <Alert
- variant="error"
- >
- Some error message
- </Alert>
- </div>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={false}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={false}
- >
- email_configuration.test.send
- </SubmitButton>
- </form>
-</div>
-`;
-
-exports[`should render correctly: sending 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={true}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={true}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={true}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={true}
- >
- email_configuration.test.send
- </SubmitButton>
- <DeferredSpinner
- className="spacer-left"
- />
- </form>
-</div>
-`;
-
-exports[`should render correctly: success 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <div
- className="form-field"
- >
- <Alert
- variant="success"
- >
- email_configuration.test.email_was_sent_to_x.email@example.com
- </Alert>
- </div>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={false}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={false}
- >
- email_configuration.test.send
- </SubmitButton>
- </form>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap
deleted file mode 100644
index 30014d31064..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap
+++ /dev/null
@@ -1,96 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
- <h2
- className="settings-sub-category-name"
- id="languages-category-title"
- >
- property.category.languages
- </h2>
- <div
- data-test="language-select"
- >
- <Select
- aria-labelledby="languages-category-title"
- className="input-large select-settings-language"
- onChange={[Function]}
- options={
- [
- {
- "label": "property.category.Java",
- "originalValue": "Java",
- "value": "java",
- },
- {
- "label": "property.category.JavaScript",
- "originalValue": "JavaScript",
- "value": "javascript",
- },
- {
- "label": "property.category.COBOL",
- "originalValue": "COBOL",
- "value": "cobol",
- },
- ]
- }
- placeholder="settings.languages.select_a_language_placeholder"
- value={
- {
- "label": "property.category.Java",
- "originalValue": "Java",
- "value": "java",
- }
- }
- />
- </div>
- <div
- className="settings-sub-category"
- >
- <CategoryDefinitionsList
- category="java"
- definitions={[]}
- />
- </div>
-</Fragment>
-`;
-
-exports[`should render correctly with an unknow language 1`] = `
-<Fragment>
- <h2
- className="settings-sub-category-name"
- id="languages-category-title"
- >
- property.category.languages
- </h2>
- <div
- data-test="language-select"
- >
- <Select
- aria-labelledby="languages-category-title"
- className="input-large select-settings-language"
- onChange={[Function]}
- options={
- [
- {
- "label": "property.category.Java",
- "originalValue": "Java",
- "value": "java",
- },
- {
- "label": "property.category.JavaScript",
- "originalValue": "JavaScript",
- "value": "javascript",
- },
- {
- "label": "property.category.COBOL",
- "originalValue": "COBOL",
- "value": "cobol",
- },
- ]
- }
- placeholder="settings.languages.select_a_language_placeholder"
- />
- </div>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-it.tsx.snap
index 24a811bedac..24a811bedac 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-it.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
deleted file mode 100644
index 0ceb31d6227..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
+++ /dev/null
@@ -1,83 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: for project 1`] = `
-<header
- className="top-bar-outer"
->
- <div
- className="top-bar"
- >
- <div
- className="top-bar-inner bordered-bottom big-padded-top padded-bottom"
- >
- <h1
- className="page-title"
- >
- project_settings.page
- </h1>
- <div
- className="page-description spacer-top"
- >
- project_settings.page.description
- </div>
- <withRouter(SettingsSearch)
- className="big-spacer-top"
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- />
- </div>
- </div>
-</header>
-`;
-
-exports[`should render correctly: global 1`] = `
-<header
- className="top-bar-outer"
->
- <div
- className="top-bar"
- >
- <div
- className="top-bar-inner bordered-bottom big-padded-top padded-bottom"
- >
- <h1
- className="page-title"
- >
- settings.page
- </h1>
- <div
- className="page-description spacer-top"
- >
- <InstanceMessage
- message="settings.page.description"
- />
- </div>
- <withRouter(SettingsSearch)
- className="big-spacer-top"
- definitions={[]}
- />
- </div>
- </div>
-</header>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsApp-test.tsx.snap
deleted file mode 100644
index 0f002e7be11..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsApp-test.tsx.snap
+++ /dev/null
@@ -1,8 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render default view correctly 1`] = `
-<withRouter(SettingsAppRenderer)
- definitions={[]}
- loading={false}
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
deleted file mode 100644
index 491942307b9..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
+++ /dev/null
@@ -1,503 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render almintegration correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="almintegration"
- >
- <withRouter(withAvailableFeaturesContext(AlmIntegration))
- categories={
- [
- "foo category",
- "general",
- ]
- }
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- selectedCategory="almintegration"
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render default view correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="general"
- >
- <CategoryDefinitionsList
- category="general"
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render default view correctly: All Categories List 1`] = `
-<div
- className="layout-page-side-outer"
->
- <div
- className="layout-page-side"
- style={
- {
- "top": 0,
- }
- }
- >
- <div
- className="layout-page-side-inner"
- >
- <withAvailableFeaturesContext(CategoriesList)
- categories={
- [
- "foo category",
- "general",
- ]
- }
- defaultCategory="general"
- selectedCategory="general"
- />
- </div>
- </div>
-</div>
-`;
-
-exports[`should render exclusions correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="exclusions"
- >
- <AnalysisScope
- categories={
- [
- "foo category",
- "general",
- ]
- }
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- selectedCategory="exclusions"
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render languages correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="languages"
- >
- <withRouter(Languages)
- categories={
- [
- "foo category",
- "general",
- ]
- }
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- selectedCategory="languages"
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render new_code_period correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="new_code_period"
- >
- <NewCodePeriod />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render pull_request_decoration_binding correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="pull_request_decoration_binding"
- >
- <CategoryDefinitionsList
- category="pull_request_decoration_binding"
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsSearchRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsSearchRenderer-test.tsx.snap
deleted file mode 100644
index a50c55c2c6a..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsSearchRenderer-test.tsx.snap
+++ /dev/null
@@ -1,134 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly when closed 1`] = `
-<OutsideClickHandler
- onClickOutside={[MockFunction]}
->
- <div
- className="dropdown"
- >
- <SearchBox
- onChange={[MockFunction]}
- onFocus={[MockFunction]}
- onKeyDown={[MockFunction]}
- placeholder="settings.search.placeholder"
- value=""
- />
- </div>
-</OutsideClickHandler>
-`;
-
-exports[`should render correctly when open: no results 1`] = `
-<OutsideClickHandler
- onClickOutside={[MockFunction]}
->
- <div
- className="dropdown"
- >
- <SearchBox
- onChange={[MockFunction]}
- onFocus={[MockFunction]}
- onKeyDown={[MockFunction]}
- placeholder="settings.search.placeholder"
- value=""
- />
- <DropdownOverlay
- noPadding={true}
- >
- <ul
- className="settings-search-results menu"
- >
- <div
- className="big-padded"
- >
- no_results
- </div>
- </ul>
- </DropdownOverlay>
- </div>
-</OutsideClickHandler>
-`;
-
-exports[`should render correctly when open: results 1`] = `
-<OutsideClickHandler
- onClickOutside={[MockFunction]}
->
- <div
- className="dropdown"
- >
- <SearchBox
- onChange={[MockFunction]}
- onFocus={[MockFunction]}
- onKeyDown={[MockFunction]}
- placeholder="settings.search.placeholder"
- value=""
- />
- <DropdownOverlay
- noPadding={true}
- >
- <ul
- className="settings-search-results menu"
- >
- <li
- className="spacer-bottom spacer-top"
- key="foo"
- >
- <ForwardRef(Link)
- onClick={[MockFunction]}
- onMouseEnter={[Function]}
- to={
- {
- "hash": "#foo",
- "pathname": "/admin/settings",
- "search": "?category=foo+category",
- }
- }
- >
- <div
- className="settings-search-result-title display-flex-space-between"
- >
- <h3>
- Foo!
- </h3>
- </div>
- <div
- className="note spacer-top"
- >
- settings.key_x.foo
- </div>
- </ForwardRef(Link)>
- </li>
- <li
- className="spacer-bottom spacer-top active"
- key="bar"
- >
- <ForwardRef(Link)
- onClick={[MockFunction]}
- onMouseEnter={[Function]}
- to={
- {
- "hash": "#bar",
- "pathname": "/admin/settings",
- "search": "?category=foo+category",
- }
- }
- >
- <div
- className="settings-search-result-title display-flex-space-between"
- >
- <h3>
- foo subCat
- </h3>
- </div>
- <div
- className="note spacer-top"
- >
- settings.key_x.bar
- </div>
- </ForwardRef(Link)>
- </li>
- </ul>
- </DropdownOverlay>
- </div>
-</OutsideClickHandler>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap
deleted file mode 100644
index 9e70241ec02..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap
+++ /dev/null
@@ -1,135 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ul
- className="settings-sub-categories-list"
->
- <li
- key="email"
- >
- <h2
- className="settings-sub-category-name"
- data-key="email"
- >
- property.category.general.email
- </h2>
- <div
- className="settings-sub-category-description markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "property.category.general.email.description",
- }
- }
- />
- <DefinitionsList
- scrollToDefinition={[Function]}
- settings={
- [
- {
- "definition": {
- "category": "general",
- "description": "When Foo then Bar",
- "fields": [],
- "key": "foo",
- "name": "Foo setting",
- "options": [],
- "subCategory": "email",
- "type": "INTEGER",
- },
- "hasValue": true,
- "inherited": true,
- "key": "foo",
- "value": "42",
- },
- ]
- }
- />
- <withCurrentUserContext(EmailForm) />
- </li>
- <li
- key="qg"
- >
- <h2
- className="settings-sub-category-name"
- data-key="qg"
- >
- property.category.general.qg
- </h2>
- <div
- className="settings-sub-category-description markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "property.category.general.qg.description",
- }
- }
- />
- <DefinitionsList
- scrollToDefinition={[Function]}
- settings={
- [
- {
- "definition": {
- "category": "general",
- "description": "awesome description",
- "fields": [],
- "key": "qg",
- "options": [],
- "subCategory": "qg",
- },
- "hasValue": true,
- "inherited": true,
- "key": "foo",
- "value": "42",
- },
- ]
- }
- />
- </li>
-</ul>
-`;
-
-exports[`should render correctly: subcategory 1`] = `
-<ul
- className="settings-sub-categories-list"
->
- <li
- key="qg"
- >
- <h2
- className="settings-sub-category-name"
- data-key="qg"
- >
- property.category.general.qg
- </h2>
- <div
- className="settings-sub-category-description markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "property.category.general.qg.description",
- }
- }
- />
- <DefinitionsList
- scrollToDefinition={[Function]}
- settings={
- [
- {
- "definition": {
- "category": "general",
- "description": "awesome description",
- "fields": [],
- "key": "qg",
- "options": [],
- "subCategory": "qg",
- },
- "hasValue": true,
- "inherited": true,
- "key": "foo",
- "value": "42",
- },
- ]
- }
- />
- </li>
-</ul>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.tsx
index 9524807ca5b..80cb2eb7fd5 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/MultiValueInput.tsx
@@ -19,7 +19,8 @@
*/
import * as React from 'react';
import { DeleteButton } from '../../../../components/controls/buttons';
-import { DefaultSpecializedInputProps, getEmptyValue } from '../../utils';
+import { translateWithParameters } from '../../../../helpers/l10n';
+import { DefaultSpecializedInputProps, getEmptyValue, getPropertyName } from '../../utils';
import PrimitiveInput from './PrimitiveInput';
export default class MultiValueInput extends React.PureComponent<DefaultSpecializedInputProps> {
@@ -56,6 +57,11 @@ export default class MultiValueInput extends React.PureComponent<DefaultSpeciali
<div className="display-inline-block spacer-left">
<DeleteButton
className="js-remove-value"
+ aria-label={translateWithParameters(
+ 'settings.definition.delete_value',
+ getPropertyName(setting.definition),
+ value
+ )}
onClick={() => this.handleDeleteValue(index)}
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/SimpleInput.tsx b/server/sonar-web/src/main/js/apps/settings/components/inputs/SimpleInput.tsx
index 098f988ea1c..4ca4a1526b7 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/inputs/SimpleInput.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/SimpleInput.tsx
@@ -20,7 +20,7 @@
import classNames from 'classnames';
import * as React from 'react';
import { KeyboardKeys } from '../../../../helpers/keycodes';
-import { DefaultSpecializedInputProps } from '../../utils';
+import { DefaultSpecializedInputProps, getPropertyName } from '../../utils';
export interface SimpleInputProps extends DefaultSpecializedInputProps {
value: string | number;
@@ -40,7 +40,7 @@ export default class SimpleInput extends React.PureComponent<SimpleInputProps> {
};
render() {
- const { autoComplete, autoFocus, className, name, value = '', type } = this.props;
+ const { autoComplete, autoFocus, className, name, value = '', setting, type } = this.props;
return (
<input
autoComplete={autoComplete}
@@ -51,6 +51,7 @@ export default class SimpleInput extends React.PureComponent<SimpleInputProps> {
onKeyDown={this.handleKeyDown}
type={type}
value={value}
+ aria-label={getPropertyName(setting.definition)}
/>
);
}
diff --git a/server/sonar-web/src/main/js/apps/settings/styles.css b/server/sonar-web/src/main/js/apps/settings/styles.css
index ad2843792e4..ecf27c1be01 100644
--- a/server/sonar-web/src/main/js/apps/settings/styles.css
+++ b/server/sonar-web/src/main/js/apps/settings/styles.css
@@ -200,6 +200,16 @@
overflow-x: hidden;
}
+.settings-search-results > li > a:hover {
+ background-color: unset;
+ border-left-color: unset;
+}
+
+.settings-search-results > li.active > a {
+ background-color: var(--neutral50);
+ border-left-color: var(--blacka60);
+}
+
.fixed-footer {
position: sticky;
bottom: 0px;
diff --git a/server/sonar-web/src/main/js/apps/settings/utils.ts b/server/sonar-web/src/main/js/apps/settings/utils.ts
index d1654f6e323..514e50acdad 100644
--- a/server/sonar-web/src/main/js/apps/settings/utils.ts
+++ b/server/sonar-web/src/main/js/apps/settings/utils.ts
@@ -56,7 +56,11 @@ export interface DefaultInputProps {
export function getPropertyName(definition: SettingDefinition) {
const key = `property.${definition.key}.name`;
- return hasMessage(key) ? translate(key) : definition.name;
+ if (hasMessage(key)) {
+ return translate(key);
+ }
+
+ return definition.name ?? definition.key;
}
export function getPropertyDescription(definition: SettingDefinition) {