From 58abe872d6edb1ec16b2e78dc5d952085be64f0d Mon Sep 17 00:00:00 2001 From: stanislavh Date: Wed, 1 Mar 2023 13:32:28 +0100 Subject: SONAR-18580 add RTL for settings (part1) --- .../src/main/js/api/mocks/SettingsServiceMock.ts | 149 +++++- .../main/js/apps/settings/__tests__/utils-test.ts | 29 +- .../apps/settings/components/AllCategoriesList.tsx | 2 +- .../apps/settings/components/DefinitionActions.tsx | 13 +- .../js/apps/settings/components/SettingsApp.tsx | 2 +- .../settings/components/SettingsAppRenderer.tsx | 4 +- .../settings/components/SettingsSearchRenderer.tsx | 6 +- .../components/SubCategoryDefinitionsList.tsx | 2 +- .../__tests__/AdditionalCategories-test.tsx | 52 -- .../__tests__/AllCategoriesList-test.tsx | 2 +- .../components/__tests__/AnalysisScope-test.tsx | 38 -- .../__tests__/CategoryDefinitionsList-test.tsx | 79 --- .../components/__tests__/Definition-it.tsx | 172 +++++++ .../components/__tests__/Definition-test.tsx | 172 ------- .../__tests__/DefinitionActions-test.tsx | 87 ---- .../__tests__/DefinitionRenderer-test.tsx | 58 --- .../components/__tests__/EmailForm-test.tsx | 90 ---- .../settings/components/__tests__/Languages-it.tsx | 132 +++++ .../components/__tests__/Languages-test.tsx | 68 --- .../components/__tests__/NewCodePeriod-it.tsx | 126 +++++ .../components/__tests__/NewCodePeriod-test.tsx | 126 ----- .../components/__tests__/PageHeader-test.tsx | 32 -- .../components/__tests__/SettingsApp-it.tsx | 191 +++++++ .../components/__tests__/SettingsApp-test.tsx | 71 --- .../__tests__/SettingsAppRenderer-test.tsx | 69 --- .../components/__tests__/SettingsSearch-test.tsx | 154 ------ .../__tests__/SettingsSearchRenderer-test.tsx | 86 ---- .../__tests__/SubCategoryDefinitionsList-test.tsx | 75 --- .../AdditionalCategories-test.tsx.snap | 150 ------ .../__snapshots__/AnalysisScope-test.tsx.snap | 77 --- .../__snapshots__/DefinitionActions-test.tsx.snap | 143 ------ .../__snapshots__/DefinitionRenderer-test.tsx.snap | 571 --------------------- .../__snapshots__/EmailForm-test.tsx.snap | 358 ------------- .../__snapshots__/Languages-test.tsx.snap | 96 ---- .../__snapshots__/NewCodePeriod-it.tsx.snap | 57 ++ .../__snapshots__/NewCodePeriod-test.tsx.snap | 57 -- .../__snapshots__/PageHeader-test.tsx.snap | 83 --- .../__snapshots__/SettingsApp-test.tsx.snap | 8 - .../SettingsAppRenderer-test.tsx.snap | 503 ------------------ .../SettingsSearchRenderer-test.tsx.snap | 134 ----- .../SubCategoryDefinitionsList-test.tsx.snap | 135 ----- .../settings/components/inputs/MultiValueInput.tsx | 8 +- .../settings/components/inputs/SimpleInput.tsx | 5 +- .../sonar-web/src/main/js/apps/settings/styles.css | 10 + .../sonar-web/src/main/js/apps/settings/utils.ts | 6 +- 45 files changed, 885 insertions(+), 3603 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/AdditionalCategories-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/CategoryDefinitionsList-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionActions-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/DefinitionRenderer-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/EmailForm-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-it.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-it.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsApp-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsAppRenderer-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearch-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/SettingsSearchRenderer-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AnalysisScope-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionActions-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/DefinitionRenderer-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/EmailForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-it.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/NewCodePeriod-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsApp-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsSearchRenderer-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SubCategoryDefinitionsList-test.tsx.snap (limited to 'server/sonar-web') 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 => { + 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 )} {showReset && ( - )} 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 { +class SettingsApp extends React.PureComponent { 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) {
{/* Adding a key to force re-rendering of the category content, so that it resets the scroll position */}
- {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 && ( -
    +
      {results && results.length > 0 ? ( results.map((r) => (
    • { +class SubCategoryDefinitionsList extends React.PureComponent { 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( - - ); -} 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 = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx new file mode 100644 index 00000000000..40869c50470 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx @@ -0,0 +1,172 @@ +/* + * 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 { getValue, resetSettingValue, setSettingValue } from '../../../../api/settings'; +import { mockDefinition, mockSettingValue } from '../../../../helpers/mocks/settings'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; +import { SettingType } from '../../../../types/settings'; +import Definition from '../Definition'; + +jest.mock('../../../../api/settings', () => ({ + getValue: jest.fn().mockResolvedValue({}), + resetSettingValue: jest.fn().mockResolvedValue(undefined), + setSettingValue: jest.fn().mockResolvedValue(undefined), +})); + +beforeAll(() => { + jest.useFakeTimers(); +}); + +afterAll(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); +}); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +describe('Handle change (and check)', () => { + it.each([ + ['empty, no default', mockDefinition(), '', 'settings.state.value_cant_be_empty_no_default'], + [ + 'empty, default', + mockDefinition({ defaultValue: 'dflt' }), + '', + 'settings.state.value_cant_be_empty', + ], + [ + 'invalid url', + mockDefinition({ key: 'sonar.core.serverBaseURL' }), + '%invalid', + 'settings.state.url_not_valid.%invalid', + ], + [ + 'valid url', + mockDefinition({ key: 'sonar.core.serverBaseURL' }), + 'http://www.sonarqube.org', + undefined, + ], + [ + 'invalid JSON', + mockDefinition({ type: SettingType.JSON }), + '{{broken: "json}', + 'Unexpected token { in JSON at position 1', + ], + ['valid JSON', mockDefinition({ type: SettingType.JSON }), '{"validJson": true}', undefined], + ])( + 'should handle change (and check value): %s', + (_caseName, definition, changedValue, expectedValidationMessage) => { + const wrapper = shallowRender({ definition }); + + wrapper.instance().handleChange(changedValue); + + expect(wrapper.state().changedValue).toBe(changedValue); + expect(wrapper.state().success).toBe(false); + expect(wrapper.state().validationMessage).toBe(expectedValidationMessage); + } + ); +}); + +it('should handle cancel', () => { + const wrapper = shallowRender(); + wrapper.setState({ changedValue: 'whatever', validationMessage: 'something wrong' }); + + wrapper.instance().handleCancel(); + + expect(wrapper.state().changedValue).toBeUndefined(); + expect(wrapper.state().validationMessage).toBeUndefined(); +}); + +describe('handleSave', () => { + it('should ignore when value unchanged', () => { + const wrapper = shallowRender(); + + wrapper.instance().handleSave(); + + expect(wrapper.state().loading).toBe(false); + expect(setSettingValue).not.toHaveBeenCalled(); + }); + + it('should handle an empty value', () => { + const wrapper = shallowRender(); + + wrapper.setState({ changedValue: '' }); + + wrapper.instance().handleSave(); + + expect(wrapper.state().loading).toBe(false); + expect(wrapper.state().validationMessage).toBe('settings.state.value_cant_be_empty'); + expect(setSettingValue).not.toHaveBeenCalled(); + }); + + it('should save and update setting value', async () => { + const settingValue = mockSettingValue(); + (getValue as jest.Mock).mockResolvedValueOnce(settingValue); + const definition = mockDefinition(); + const wrapper = shallowRender({ definition }); + + wrapper.setState({ changedValue: 'new value' }); + + wrapper.instance().handleSave(); + + expect(wrapper.state().loading).toBe(true); + + await waitAndUpdate(wrapper); + + expect(setSettingValue).toHaveBeenCalledWith(definition, 'new value', undefined); + expect(getValue).toHaveBeenCalledWith({ key: definition.key, component: undefined }); + expect(wrapper.state().changedValue).toBeUndefined(); + expect(wrapper.state().loading).toBe(false); + expect(wrapper.state().success).toBe(true); + expect(wrapper.state().settingValue).toBe(settingValue); + + jest.runAllTimers(); + expect(wrapper.state().success).toBe(false); + }); +}); + +it('should reset and update setting value', async () => { + const settingValue = mockSettingValue(); + (getValue as jest.Mock).mockResolvedValueOnce(settingValue); + const definition = mockDefinition(); + const wrapper = shallowRender({ definition }); + + wrapper.instance().handleReset(); + + expect(wrapper.state().loading).toBe(true); + + await waitAndUpdate(wrapper); + + expect(resetSettingValue).toHaveBeenCalledWith({ keys: definition.key, component: undefined }); + expect(getValue).toHaveBeenCalledWith({ key: definition.key, component: undefined }); + expect(wrapper.state().changedValue).toBeUndefined(); + expect(wrapper.state().loading).toBe(false); + expect(wrapper.state().success).toBe(true); + expect(wrapper.state().settingValue).toBe(settingValue); + + jest.runAllTimers(); + expect(wrapper.state().success).toBe(false); +}); + +function shallowRender(props: Partial = {}) { + return shallow(); +} 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-test.tsx deleted file mode 100644 index 40869c50470..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-test.tsx +++ /dev/null @@ -1,172 +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 { getValue, resetSettingValue, setSettingValue } from '../../../../api/settings'; -import { mockDefinition, mockSettingValue } from '../../../../helpers/mocks/settings'; -import { waitAndUpdate } from '../../../../helpers/testUtils'; -import { SettingType } from '../../../../types/settings'; -import Definition from '../Definition'; - -jest.mock('../../../../api/settings', () => ({ - getValue: jest.fn().mockResolvedValue({}), - resetSettingValue: jest.fn().mockResolvedValue(undefined), - setSettingValue: jest.fn().mockResolvedValue(undefined), -})); - -beforeAll(() => { - jest.useFakeTimers(); -}); - -afterAll(() => { - jest.runOnlyPendingTimers(); - jest.useRealTimers(); -}); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -describe('Handle change (and check)', () => { - it.each([ - ['empty, no default', mockDefinition(), '', 'settings.state.value_cant_be_empty_no_default'], - [ - 'empty, default', - mockDefinition({ defaultValue: 'dflt' }), - '', - 'settings.state.value_cant_be_empty', - ], - [ - 'invalid url', - mockDefinition({ key: 'sonar.core.serverBaseURL' }), - '%invalid', - 'settings.state.url_not_valid.%invalid', - ], - [ - 'valid url', - mockDefinition({ key: 'sonar.core.serverBaseURL' }), - 'http://www.sonarqube.org', - undefined, - ], - [ - 'invalid JSON', - mockDefinition({ type: SettingType.JSON }), - '{{broken: "json}', - 'Unexpected token { in JSON at position 1', - ], - ['valid JSON', mockDefinition({ type: SettingType.JSON }), '{"validJson": true}', undefined], - ])( - 'should handle change (and check value): %s', - (_caseName, definition, changedValue, expectedValidationMessage) => { - const wrapper = shallowRender({ definition }); - - wrapper.instance().handleChange(changedValue); - - expect(wrapper.state().changedValue).toBe(changedValue); - expect(wrapper.state().success).toBe(false); - expect(wrapper.state().validationMessage).toBe(expectedValidationMessage); - } - ); -}); - -it('should handle cancel', () => { - const wrapper = shallowRender(); - wrapper.setState({ changedValue: 'whatever', validationMessage: 'something wrong' }); - - wrapper.instance().handleCancel(); - - expect(wrapper.state().changedValue).toBeUndefined(); - expect(wrapper.state().validationMessage).toBeUndefined(); -}); - -describe('handleSave', () => { - it('should ignore when value unchanged', () => { - const wrapper = shallowRender(); - - wrapper.instance().handleSave(); - - expect(wrapper.state().loading).toBe(false); - expect(setSettingValue).not.toHaveBeenCalled(); - }); - - it('should handle an empty value', () => { - const wrapper = shallowRender(); - - wrapper.setState({ changedValue: '' }); - - wrapper.instance().handleSave(); - - expect(wrapper.state().loading).toBe(false); - expect(wrapper.state().validationMessage).toBe('settings.state.value_cant_be_empty'); - expect(setSettingValue).not.toHaveBeenCalled(); - }); - - it('should save and update setting value', async () => { - const settingValue = mockSettingValue(); - (getValue as jest.Mock).mockResolvedValueOnce(settingValue); - const definition = mockDefinition(); - const wrapper = shallowRender({ definition }); - - wrapper.setState({ changedValue: 'new value' }); - - wrapper.instance().handleSave(); - - expect(wrapper.state().loading).toBe(true); - - await waitAndUpdate(wrapper); - - expect(setSettingValue).toHaveBeenCalledWith(definition, 'new value', undefined); - expect(getValue).toHaveBeenCalledWith({ key: definition.key, component: undefined }); - expect(wrapper.state().changedValue).toBeUndefined(); - expect(wrapper.state().loading).toBe(false); - expect(wrapper.state().success).toBe(true); - expect(wrapper.state().settingValue).toBe(settingValue); - - jest.runAllTimers(); - expect(wrapper.state().success).toBe(false); - }); -}); - -it('should reset and update setting value', async () => { - const settingValue = mockSettingValue(); - (getValue as jest.Mock).mockResolvedValueOnce(settingValue); - const definition = mockDefinition(); - const wrapper = shallowRender({ definition }); - - wrapper.instance().handleReset(); - - expect(wrapper.state().loading).toBe(true); - - await waitAndUpdate(wrapper); - - expect(resetSettingValue).toHaveBeenCalledWith({ keys: definition.key, component: undefined }); - expect(getValue).toHaveBeenCalledWith({ key: definition.key, component: undefined }); - expect(wrapper.state().changedValue).toBeUndefined(); - expect(wrapper.state().loading).toBe(false); - expect(wrapper.state().success).toBe(true); - expect(wrapper.state().settingValue).toBe(settingValue); - - jest.runAllTimers(); - expect(wrapper.state().success).toBe(false); -}); - -function shallowRender(props: Partial = {}) { - return shallow(); -} 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( - - ); -} 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 = {}) { - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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 = {}, + component = mockComponent() +) { + return renderApp( + '/', + + ); +} 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 = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx new file mode 100644 index 00000000000..c95f2e18bb8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx @@ -0,0 +1,126 @@ +/* + * 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 { getNewCodePeriod, setNewCodePeriod } from '../../../../api/newCodePeriod'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; +import NewCodePeriod from '../NewCodePeriod'; + +jest.mock('../../../../api/newCodePeriod', () => ({ + getNewCodePeriod: jest.fn().mockResolvedValue({}), + setNewCodePeriod: jest.fn(() => Promise.resolve()), +})); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('should load the current new code period on mount', async () => { + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(getNewCodePeriod).toHaveBeenCalledTimes(1); + expect(wrapper.state('currentSetting')).toBe('PREVIOUS_VERSION'); +}); + +it('should load the current new code period with value on mount', async () => { + (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); + + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(getNewCodePeriod).toHaveBeenCalledTimes(1); + expect(wrapper.state('currentSetting')).toBe('NUMBER_OF_DAYS'); + expect(wrapper.state('days')).toBe('42'); +}); + +it('should only show the save button after changing the setting', async () => { + (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'PREVIOUS_VERSION' }); + + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + + expect(wrapper.state('selected')).toBe('PREVIOUS_VERSION'); + expect(wrapper.find('SubmitButton')).toHaveLength(0); + + wrapper.instance().onSelectSetting('NUMBER_OF_DAYS'); + await waitAndUpdate(wrapper); + + expect(wrapper.find('SubmitButton')).toHaveLength(1); +}); + +it('should disable the button if the days are invalid', async () => { + (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); + + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + + wrapper.instance().onSelectDays('asd'); + await waitAndUpdate(wrapper); + + expect(wrapper.find('SubmitButton').first().prop('disabled')).toBe(true); + + wrapper.instance().onSelectDays('23'); + await waitAndUpdate(wrapper); + + expect(wrapper.find('SubmitButton').first().prop('disabled')).toBe(false); +}); + +it('should submit correctly', async () => { + (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); + + const preventDefault = jest.fn(); + + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + wrapper.instance().onSelectSetting('PREVIOUS_VERSION'); + await waitAndUpdate(wrapper); + + wrapper.find('form').simulate('submit', { preventDefault }); + + expect(preventDefault).toHaveBeenCalledTimes(1); + expect(setNewCodePeriod).toHaveBeenCalledWith({ type: 'PREVIOUS_VERSION', value: undefined }); + await waitAndUpdate(wrapper); + expect(wrapper.state('currentSetting')).toEqual(wrapper.state('selected')); +}); + +it('should submit correctly with days', async () => { + (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); + + const preventDefault = jest.fn(); + + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + wrapper.instance().onSelectDays('66'); + await waitAndUpdate(wrapper); + + wrapper.find('form').simulate('submit', { preventDefault }); + + expect(preventDefault).toHaveBeenCalledTimes(1); + expect(setNewCodePeriod).toHaveBeenCalledWith({ type: 'NUMBER_OF_DAYS', value: '66' }); + await waitAndUpdate(wrapper); + expect(wrapper.state('currentSetting')).toEqual(wrapper.state('selected')); +}); + +function shallowRender() { + return shallow(); +} 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-test.tsx deleted file mode 100644 index c95f2e18bb8..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-test.tsx +++ /dev/null @@ -1,126 +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 { getNewCodePeriod, setNewCodePeriod } from '../../../../api/newCodePeriod'; -import { waitAndUpdate } from '../../../../helpers/testUtils'; -import NewCodePeriod from '../NewCodePeriod'; - -jest.mock('../../../../api/newCodePeriod', () => ({ - getNewCodePeriod: jest.fn().mockResolvedValue({}), - setNewCodePeriod: jest.fn(() => Promise.resolve()), -})); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should load the current new code period on mount', async () => { - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - expect(getNewCodePeriod).toHaveBeenCalledTimes(1); - expect(wrapper.state('currentSetting')).toBe('PREVIOUS_VERSION'); -}); - -it('should load the current new code period with value on mount', async () => { - (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); - - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - expect(getNewCodePeriod).toHaveBeenCalledTimes(1); - expect(wrapper.state('currentSetting')).toBe('NUMBER_OF_DAYS'); - expect(wrapper.state('days')).toBe('42'); -}); - -it('should only show the save button after changing the setting', async () => { - (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'PREVIOUS_VERSION' }); - - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - - expect(wrapper.state('selected')).toBe('PREVIOUS_VERSION'); - expect(wrapper.find('SubmitButton')).toHaveLength(0); - - wrapper.instance().onSelectSetting('NUMBER_OF_DAYS'); - await waitAndUpdate(wrapper); - - expect(wrapper.find('SubmitButton')).toHaveLength(1); -}); - -it('should disable the button if the days are invalid', async () => { - (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); - - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - - wrapper.instance().onSelectDays('asd'); - await waitAndUpdate(wrapper); - - expect(wrapper.find('SubmitButton').first().prop('disabled')).toBe(true); - - wrapper.instance().onSelectDays('23'); - await waitAndUpdate(wrapper); - - expect(wrapper.find('SubmitButton').first().prop('disabled')).toBe(false); -}); - -it('should submit correctly', async () => { - (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); - - const preventDefault = jest.fn(); - - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - wrapper.instance().onSelectSetting('PREVIOUS_VERSION'); - await waitAndUpdate(wrapper); - - wrapper.find('form').simulate('submit', { preventDefault }); - - expect(preventDefault).toHaveBeenCalledTimes(1); - expect(setNewCodePeriod).toHaveBeenCalledWith({ type: 'PREVIOUS_VERSION', value: undefined }); - await waitAndUpdate(wrapper); - expect(wrapper.state('currentSetting')).toEqual(wrapper.state('selected')); -}); - -it('should submit correctly with days', async () => { - (getNewCodePeriod as jest.Mock).mockResolvedValue({ type: 'NUMBER_OF_DAYS', value: '42' }); - - const preventDefault = jest.fn(); - - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - wrapper.instance().onSelectDays('66'); - await waitAndUpdate(wrapper); - - wrapper.find('form').simulate('submit', { preventDefault }); - - expect(preventDefault).toHaveBeenCalledTimes(1); - expect(setNewCodePeriod).toHaveBeenCalledWith({ type: 'NUMBER_OF_DAYS', value: '66' }); - await waitAndUpdate(wrapper); - expect(wrapper.state('currentSetting')).toEqual(wrapper.state('selected')); -}); - -function shallowRender() { - return shallow(); -} 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 = {}) { - return shallow(); -} 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 = () => {routes()}; + 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 = {}) { - return shallow(); -} 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 = {}) { - const definitions = [mockDefinition(), mockDefinition({ key: 'bar', category: 'general' })]; - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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`] = ` - -`; - -exports[`should render additional categories component correctly 2`] = ``; - -exports[`should render additional categories component correctly 3`] = ` - -`; - -exports[`should render additional categories component correctly 4`] = ` - -`; - -exports[`should render additional categories component correctly 5`] = ` - -`; - -exports[`should render additional categories component correctly 6`] = ` - -`; 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`] = ` - -

      - settings.analysis_scope.wildcards.introduction - - learn_more - -

      - - - - - - - - - - - - - - - -
      - * - - settings.analysis_scope.wildcards.zero_more_char -
      - ** - - settings.analysis_scope.wildcards.zero_more_dir -
      - ? - - settings.analysis_scope.wildcards.single_char -
      -
      - -
      -
      -`; 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`] = ` - -
      - - - cancel - -
      -
      -`; - -exports[`displays cancel button when value changed and has error 1`] = ` - -
      - - - cancel - -
      -
      -`; - -exports[`displays cancel button when value changed and no error 1`] = ` - -
      - - - cancel - -
      -
      -`; - -exports[`displays default message when value is default 1`] = ` - -
      - settings._default -
      -
      - - - default - : - settings.default.no_value - -
      -
      -`; - -exports[`displays reset button when empty and not default 1`] = ` - -
      - - - default - : - settings.default.no_value - -
      -
      -`; - -exports[`displays save button when it can be saved 1`] = ` - -
      - - - cancel - -
      -
      -`; 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`] = ` -
      -
      -

      - property.foo.name -

      -
      - -
      - settings.key_x.foo -
      -
      -
      -
      -
      -
      - - - -
      -
      -`; - -exports[`should render correctly: in error 1`] = ` -
      -
      -

      - property.foo.name -

      -
      - -
      - settings.key_x.foo -
      -
      -
      -
      -
      - - - - settings.state.validation_failed.validation message - - -
      -
      - - - -
      -
      -`; - -exports[`should render correctly: loading 1`] = ` -
      -
      -

      - property.foo.name -

      -
      - -
      - settings.key_x.foo -
      -
      -
      -
      -
      - - - settings.state.saving - -
      -
      - - - -
      -
      -`; - -exports[`should render correctly: original value 1`] = ` -
      -
      -

      - property.foo.name -

      -
      - -
      - settings.key_x.foo -
      -
      -
      -
      -
      -
      - - - -
      -
      -`; - -exports[`should render correctly: success 1`] = ` -
      -
      -

      - property.foo.name -

      -
      - -
      - settings.key_x.foo -
      -
      -
      -
      -
      - - - settings.state.saved - -
      -
      - - - -
      -
      -`; - -exports[`should render correctly: with description 1`] = ` -
      -
      -

      - property.foo.name -

      -
      - -
      - settings.key_x.foo -
      -
      -
      -
      -
      -
      - - - -
      -
      -`; 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`] = ` -
      -
      -

      - email_configuration.test.title -

      -
      -
      - -
      - - -
      -
      - - -
      -
      - -