*/
import { cloneDeep } from 'lodash';
import { HousekeepingPolicy } from '../../apps/audit-logs/utils';
+import { mockDefinition } from '../../helpers/mocks/settings';
import { BranchParameters } from '../../types/branch-like';
-import { SettingsKey, SettingValue } from '../../types/settings';
-import { getAllValues, getValue, getValues } from '../settings';
+import {
+ ExtendedSettingDefinition,
+ SettingDefinition,
+ SettingsKey,
+ SettingType,
+ SettingValue,
+} from '../../types/settings';
+import {
+ getAllValues,
+ getDefinitions,
+ getValue,
+ getValues,
+ resetSettingValue,
+ setSettingValue,
+} from '../settings';
+
+const isEmptyString = (i: string) => i.trim() === '';
+
+export const DEFAULT_DEFINITIONS_MOCK = [
+ mockDefinition({
+ category: 'general',
+ key: 'sonar.announcement.message',
+ subCategory: 'Announcement',
+ name: 'Announcement message',
+ description: 'Enter message',
+ defaultValue: '.js,.jsx,.cjs,.vue,.mjs',
+ type: SettingType.TEXT,
+ }),
+ mockDefinition({
+ category: 'general',
+ key: 'sonar.ce.parallelProjectTasks',
+ subCategory: 'Compute Engine',
+ name: 'Run analysis in paralel',
+ description: 'Enter message',
+ defaultValue: '.js,.jsx,.cjs,.vue,.mjs',
+ type: SettingType.TEXT,
+ }),
+ mockDefinition({
+ category: 'javascript',
+ key: 'sonar.javascript.globals',
+ subCategory: 'General',
+ name: 'Global Variables',
+ description: 'List of Global variables',
+ multiValues: true,
+ defaultValue: 'angular,google,d3',
+ }),
+ mockDefinition({
+ category: 'javascript',
+ key: 'sonar.javascript.file.suffixes',
+ subCategory: 'General',
+ name: 'JavaScript File Suffixes',
+ description: 'List of suffixes for files to analyze',
+ multiValues: true,
+ defaultValue: '.js,.jsx,.cjs,.vue,.mjs',
+ }),
+ mockDefinition({
+ category: 'External Analyzers',
+ key: 'sonar.androidLint.reportPaths',
+ subCategory: 'Android',
+ name: 'Android Lint Report Files',
+ description: 'Paths to xml files',
+ multiValues: true,
+ }),
+];
export default class SettingsServiceMock {
- settingValues: SettingValue[];
- defaultValues: SettingValue[] = [
+ #defaultValues: SettingValue[] = [
{
key: SettingsKey.AuditHouseKeeping,
value: HousekeepingPolicy.Weekly,
},
+ {
+ key: 'sonar.javascript.globals',
+ values: ['angular', 'google', 'd3'],
+ },
];
+ #settingValues: SettingValue[] = cloneDeep(this.#defaultValues);
+
+ #definitions: ExtendedSettingDefinition[] = cloneDeep(DEFAULT_DEFINITIONS_MOCK);
+
constructor() {
- this.settingValues = cloneDeep(this.defaultValues);
- (getValue as jest.Mock).mockImplementation(this.handleGetValue);
- (getValues as jest.Mock).mockImplementation(this.handleGetValues);
- (getAllValues as jest.Mock).mockImplementation(this.handleGetAllValues);
+ jest.mocked(getDefinitions).mockImplementation(this.handleGetDefinitions);
+ jest.mocked(getValue).mockImplementation(this.handleGetValue);
+ jest.mocked(getValues).mockImplementation(this.handleGetValues);
+ jest.mocked(getAllValues).mockImplementation(this.handleGetAllValues);
+ jest.mocked(setSettingValue).mockImplementation(this.handleSetSettingValue);
+ jest.mocked(resetSettingValue).mockImplementation(this.handleResetSettingValue);
}
handleGetValue = (data: { key: string; component?: string } & BranchParameters) => {
- const setting = this.settingValues.find((s) => s.key === data.key);
+ const setting = this.#settingValues.find((s) => s.key === data.key) as SettingValue;
return this.reply(setting);
};
handleGetValues = (data: { keys: string[]; component?: string } & BranchParameters) => {
- const settings = this.settingValues.filter((s) => data.keys.includes(s.key));
+ const settings = this.#settingValues.filter((s) => data.keys.includes(s.key));
return this.reply(settings);
};
handleGetAllValues = () => {
- return this.reply(this.settingValues);
+ return this.reply(this.#settingValues);
+ };
+
+ handleGetDefinitions = () => {
+ return this.reply(this.#definitions);
+ };
+
+ handleSetSettingValue = (definition: SettingDefinition, value: any): Promise<void> => {
+ if (
+ (typeof value === 'string' && isEmptyString(value)) ||
+ (value instanceof Array && value.some(isEmptyString))
+ ) {
+ throw new ResponseError('validation error', {
+ errors: [{ msg: 'A non empty value must be provided' }],
+ });
+ }
+
+ this.set(definition.key, value);
+
+ return this.reply(undefined);
+ };
+
+ handleResetSettingValue = (data: { keys: string; component?: string } & BranchParameters) => {
+ const setting = this.#settingValues.find((s) => s.key === data.keys) as SettingValue;
+ const definition = this.#definitions.find(
+ (d) => d.key === data.keys
+ ) as ExtendedSettingDefinition;
+ if (definition.multiValues === true) {
+ setting.values = definition.defaultValue?.split(',') ?? [];
+ } else {
+ setting.value = definition.defaultValue ?? '';
+ }
+
+ return this.reply(undefined);
};
emptySettings = () => {
- this.settingValues = [];
+ this.#settingValues = [];
return this;
};
- set = (key: SettingsKey, value: string) => {
- const setting = this.settingValues.find((s) => s.key === key);
+ set = (key: string | SettingsKey, value: any) => {
+ const setting = this.#settingValues.find((s) => s.key === key);
if (setting) {
setting.value = value;
+ setting.values = value;
} else {
- this.settingValues.push({ key, value });
+ this.#settingValues.push({ key, value });
}
return this;
};
reset = () => {
- this.settingValues = cloneDeep(this.defaultValues);
+ this.#settingValues = cloneDeep(this.#defaultValues);
+ this.#definitions = cloneDeep(DEFAULT_DEFINITIONS_MOCK);
return this;
};
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);
+}
* 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 {
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,
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 = {
selectedCategory: string;
}
-export function CategoriesList(props: CategoriesListProps) {
+function CategoriesList(props: CategoriesListProps) {
const { categories, component, defaultCategory, selectedCategory } = props;
const categoriesWithName = categories
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;
)}
{showReset && (
- <Button className="spacer-right" onClick={this.handleReset}>
+ <Button
+ className="spacer-right"
+ aria-label={translateWithParameters(
+ 'settings.definition.reset',
+ getPropertyName(setting.definition)
+ )}
+ onClick={this.handleReset}
+ >
{translate('reset_verb')}
</Button>
)}
loading: boolean;
}
-export class SettingsApp extends React.PureComponent<Props, State> {
+class SettingsApp extends React.PureComponent<Props, State> {
mounted = false;
state: State = { definitions: [], loading: true };
location: Location;
}
-export function SettingsAppRenderer(props: SettingsAppRendererProps) {
+function SettingsAppRenderer(props: SettingsAppRendererProps) {
const { definitions, component, loading, location } = props;
const categories = React.useMemo(() => {
<div className="layout-page-main-inner">
{/* Adding a key to force re-rendering of the category content, so that it resets the scroll position */}
<div className="big-padded" key={selectedCategory}>
- {foundAdditionalCategory && shouldRenderAdditionalCategory ? (
+ {shouldRenderAdditionalCategory ? (
foundAdditionalCategory.renderComponent({
categories,
component,
/>
{showResults && (
<DropdownOverlay noPadding={true}>
- <ul className="settings-search-results menu" ref={scrollableNodeRef}>
+ <ul
+ className="settings-search-results menu"
+ title={translate('settings.search.results')}
+ ref={scrollableNodeRef}
+ >
{results && results.length > 0 ? (
results.map((r) => (
<li
displaySubCategoryTitle?: boolean;
}
-export class SubCategoryDefinitionsList extends React.PureComponent<SubCategoryDefinitionsListProps> {
+class SubCategoryDefinitionsList extends React.PureComponent<SubCategoryDefinitionsListProps> {
componentDidUpdate(prevProps: SubCategoryDefinitionsListProps) {
const { hash } = this.props.location;
if (hash && prevProps.location.hash !== hash) {
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { 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();
-});
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: [
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { AnalysisScope } from '../AnalysisScope';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
- return shallow(
- <AnalysisScope
- categories={[]}
- component={mockComponent()}
- definitions={[]}
- selectedCategory="TEST"
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { getValues } from '../../../../api/settings';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockDefinition, mockSettingValue } from '../../../../helpers/mocks/settings';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import CategoryDefinitionsList from '../CategoryDefinitionsList';
-
-jest.mock('../../../../api/settings', () => ({
- getValues: jest.fn().mockResolvedValue([]),
-}));
-
-it('should load settings values', async () => {
- const settings = [mockSettingValue({ key: 'yes' }), mockSettingValue({ key: 'yesagain' })];
- (getValues as jest.Mock).mockResolvedValueOnce(settings);
-
- const definitions = [
- mockDefinition({ category: 'general', key: 'yes' }),
- mockDefinition({ category: 'other', key: 'nope' }),
- mockDefinition({ category: 'general', key: 'yesagain' }),
- ];
-
- const wrapper = shallowRender({
- definitions,
- });
-
- await waitAndUpdate(wrapper);
-
- expect(getValues).toHaveBeenCalledWith({ keys: ['yes', 'yesagain'], component: undefined });
-
- expect(wrapper.state().settings).toEqual([
- { definition: definitions[0], settingValue: settings[0] },
- { definition: definitions[2], settingValue: settings[1] },
- ]);
-});
-
-it('should reload on category change', async () => {
- const definitions = [
- mockDefinition({ category: 'general', key: 'yes' }),
- mockDefinition({ category: 'other', key: 'nope' }),
- mockDefinition({ category: 'general', key: 'yesagain' }),
- ];
- const wrapper = shallowRender({ component: mockComponent({ key: 'comp-key' }), definitions });
-
- await waitAndUpdate(wrapper);
-
- expect(getValues).toHaveBeenCalledWith({ keys: ['yes', 'yesagain'], component: 'comp-key' });
-
- wrapper.setProps({ category: 'other' });
-
- await waitAndUpdate(wrapper);
-
- expect(getValues).toHaveBeenCalledWith({ keys: ['nope'], component: 'comp-key' });
-});
-
-function shallowRender(props: Partial<CategoryDefinitionsList['props']> = {}) {
- return shallow<CategoryDefinitionsList>(
- <CategoryDefinitionsList category="general" definitions={[]} {...props} />
- );
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { 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<Definition['props']> = {}) {
+ return shallow<Definition>(<Definition definition={mockDefinition()} {...props} />);
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { 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<Definition['props']> = {}) {
- return shallow<Definition>(<Definition definition={mockDefinition()} {...props} />);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { ExtendedSettingDefinition, SettingType } from '../../../../types/settings';
-import DefinitionActions from '../DefinitionActions';
-
-const definition: ExtendedSettingDefinition = {
- category: 'baz',
- description: 'lorem',
- fields: [],
- key: 'key',
- name: 'foobar',
- options: [],
- subCategory: 'bar',
- type: SettingType.STRING,
-};
-
-const settings = {
- key: 'key',
- hasValue: true,
- definition,
- value: 'baz',
-};
-
-it('displays default message when value is default', () => {
- const wrapper = shallowRender('', false, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays save button when it can be saved', () => {
- const wrapper = shallowRender('foo', false, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays cancel button when value changed and no error', () => {
- const wrapper = shallowRender('foo', false, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays cancel button when value changed and has error', () => {
- const wrapper = shallowRender('foo', true, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('disables save button on error', () => {
- const wrapper = shallowRender('foo', true, true);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('displays reset button when empty and not default', () => {
- const wrapper = shallowRender('', true, false);
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(changedValue: string, hasError: boolean, isDefault: boolean) {
- return shallow(
- <DefinitionActions
- isEditing={false}
- changedValue={changedValue}
- hasError={hasError}
- hasValueChanged={changedValue !== ''}
- isDefault={isDefault}
- onCancel={jest.fn()}
- onReset={jest.fn()}
- onSave={jest.fn()}
- setting={settings}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockDefinition, mockSettingValue } from '../../../../helpers/mocks/settings';
-import DefinitionRenderer, { DefinitionRendererProps } from '../DefinitionRenderer';
-
-it('should render correctly', () => {
- expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
- expect(
- shallowRender({ definition: mockDefinition({ description: 'description' }) })
- ).toMatchSnapshot('with description');
- expect(
- shallowRender({
- validationMessage: 'validation message',
- })
- ).toMatchSnapshot('in error');
- expect(shallowRender({ success: true })).toMatchSnapshot('success');
- expect(
- shallowRender({ settingValue: mockSettingValue({ key: 'foo', value: 'original value' }) })
- ).toMatchSnapshot('original value');
-
- expect(shallowRender({ changedValue: 'new value' })).toMatchSnapshot('changed value');
-});
-
-function shallowRender(props: Partial<DefinitionRendererProps> = {}) {
- return shallow<DefinitionRendererProps>(
- <DefinitionRenderer
- isEditing={false}
- definition={mockDefinition()}
- loading={false}
- onCancel={jest.fn()}
- onChange={jest.fn()}
- onEditing={jest.fn()}
- onReset={jest.fn()}
- onSave={jest.fn()}
- success={false}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { sendTestEmail } from '../../../../api/settings';
-import { mockLoggedInUser } from '../../../../helpers/testMocks';
-import { change, submit, waitAndUpdate } from '../../../../helpers/testUtils';
-import { EmailForm } from '../EmailForm';
-
-jest.mock('../../../../helpers/request', () => ({
- parseError: jest.fn().mockResolvedValue('Error message'),
-}));
-
-jest.mock('../../../../api/settings', () => ({
- sendTestEmail: jest.fn().mockResolvedValue(null),
-}));
-
-it('should render correctly', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot('default');
- wrapper.setState({ loading: true });
- expect(wrapper).toMatchSnapshot('sending');
- wrapper.setState({ loading: false, success: 'email@example.com' });
- expect(wrapper).toMatchSnapshot('success');
- wrapper.setState({ success: undefined, error: 'Some error message' });
- expect(wrapper).toMatchSnapshot('error');
-});
-
-it('should correctly control the inputs', () => {
- const wrapper = shallowRender();
-
- change(wrapper.find('#test-email-to'), 'new@recipient.com');
- expect(wrapper.state().recipient).toBe('new@recipient.com');
-
- change(wrapper.find('#test-email-subject'), 'New subject');
- expect(wrapper.state().subject).toBe('New subject');
-
- change(wrapper.find('#test-email-message'), 'New message');
- expect(wrapper.state().message).toBe('New message');
-});
-
-it('should correctly test the email sending', async () => {
- const wrapper = shallowRender();
-
- submit(wrapper.find('form'));
- expect(sendTestEmail).toHaveBeenCalledWith(
- 'luke@skywalker.biz',
- 'email_configuration.test.subject',
- 'email_configuration.test.message_text'
- );
- expect(wrapper.state().loading).toBe(true);
-
- await waitAndUpdate(wrapper);
-
- expect(wrapper.state().loading).toBe(false);
- expect(wrapper.state().error).toBeUndefined();
- expect(wrapper.state().success).toBe('luke@skywalker.biz');
-
- (sendTestEmail as jest.Mock).mockRejectedValueOnce(null);
-
- submit(wrapper.find('form'));
-
- await waitAndUpdate(wrapper);
-
- expect(wrapper.state().success).toBeUndefined();
- expect(wrapper.state().error).toBe('Error message');
-});
-
-function shallowRender(props: Partial<EmailForm['props']> = {}) {
- return shallow<EmailForm>(
- <EmailForm currentUser={mockLoggedInUser({ email: 'luke@skywalker.biz' })} {...props} />
- );
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { last } from 'lodash';
+import React from 'react';
+import { byRole, byText } from 'testing-library-selector';
+import SettingsServiceMock, {
+ DEFAULT_DEFINITIONS_MOCK,
+} from '../../../../api/mocks/SettingsServiceMock';
+import { mockComponent } from '../../../../helpers/mocks/component';
+import { renderApp } from '../../../../helpers/testReactTestingUtils';
+import { AdditionalCategoryComponentProps } from '../AdditionalCategories';
+import Languages from '../Languages';
+
+jest.mock('../../../../api/settings');
+
+let settingsMock: SettingsServiceMock;
+
+beforeAll(() => {
+ settingsMock = new SettingsServiceMock();
+});
+
+afterEach(() => {
+ settingsMock.reset();
+});
+
+beforeEach(jest.clearAllMocks);
+
+const ui = {
+ languagesHeading: byRole('heading', { name: 'property.category.languages' }),
+ languagesSelect: byRole('combobox', { name: 'property.category.languages' }),
+ jsGeneralSubCategoryHeading: byRole('heading', { name: 'property.category.javascript.General' }),
+ jsGlobalVariablesHeading: byRole('heading', {
+ name: 'property.sonar.javascript.globals.name',
+ }),
+ jsGlobalVariablesDescription: byText('List of Global variables'),
+ jsFileSuffixesHeading: byRole('heading', {
+ name: 'property.sonar.javascript.file.suffixes.name',
+ }),
+ jsGlobalVariablesInput: byRole('textbox', { name: 'property.sonar.javascript.globals.name' }),
+ jsResetGlobalVariablesButton: byRole('button', {
+ name: 'settings.definition.reset.property.sonar.javascript.globals.name',
+ }),
+
+ validationMsg: byText('settings.state.validation_failed.A non empty value must be provided'),
+ saveButton: byRole('button', { name: 'save' }),
+ cancelButton: byRole('button', { name: 'cancel' }),
+ resetButton: byRole('button', { name: 'reset_verb' }),
+};
+
+it('renders Language with selected Javascript category', async () => {
+ renderLanguages({ selectedCategory: 'javascript' });
+
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ expect(await ui.jsGeneralSubCategoryHeading.find()).toBeInTheDocument();
+ expect(ui.jsGlobalVariablesHeading.get()).toBeInTheDocument();
+ expect(ui.jsFileSuffixesHeading.get()).toBeInTheDocument();
+});
+
+it('render Language without definitions', async () => {
+ renderLanguages({ selectedCategory: 'niceLanguage' });
+
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ expect(screen.queryByText(/niceLanguage/)).not.toBeInTheDocument();
+});
+
+it('can save/reset/cancel or see error for custom mocked multi values definition Global Variables', async () => {
+ const user = userEvent.setup();
+ renderLanguages({ selectedCategory: 'javascript' });
+
+ const jsVarsInputs = await ui.jsGlobalVariablesInput.findAll();
+ const lastInput = last(jsVarsInputs);
+ // Adding new js variable (multi-values input)
+ expect(jsVarsInputs).toHaveLength(4);
+
+ // Should see a validation message on typing empty string
+ await user.type(lastInput as HTMLElement, ' ');
+ await user.click(await ui.saveButton.find());
+ expect(await ui.validationMsg.find()).toBeInTheDocument();
+
+ // Should save variable
+ await user.type(lastInput as HTMLElement, 'Testing');
+ await user.click(await ui.saveButton.find());
+ expect(ui.validationMsg.query()).not.toBeInTheDocument();
+ expect(lastInput).toHaveValue(' Testing');
+
+ // Should reset to previous state on clicking cancel
+ await user.type(last(ui.jsGlobalVariablesInput.getAll()) as HTMLElement, 'Testing2');
+ await user.click(ui.cancelButton.get());
+ expect(last(ui.jsGlobalVariablesInput.getAll())).not.toHaveValue('Testing2');
+
+ // Clicking reset opens dialog and reset to default on confirm
+ const defaultValues = ['angular', 'google', 'd3', ''];
+ await user.click(ui.jsResetGlobalVariablesButton.get());
+ await user.click(ui.resetButton.get());
+ const newInputs = ui.jsGlobalVariablesInput.getAll();
+ defaultValues.forEach((value, index) => expect(newInputs[index]).toHaveValue(value));
+});
+
+function renderLanguages(
+ overrides: Partial<AdditionalCategoryComponentProps> = {},
+ component = mockComponent()
+) {
+ return renderApp(
+ '/',
+ <Languages
+ definitions={DEFAULT_DEFINITIONS_MOCK}
+ component={component}
+ categories={['javascript', 'sjava']}
+ selectedCategory=""
+ {...overrides}
+ />
+ );
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import Select from '../../../../components/controls/Select';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockLocation, mockRouter } from '../../../../helpers/testMocks';
-import CategoryDefinitionsList from '../CategoryDefinitionsList';
-import { Languages, LanguagesProps } from '../Languages';
-
-it('should render correctly', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render correctly with an unknow language', () => {
- const wrapper = shallowRender({ selectedCategory: 'unknown' });
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should correctly handle a change of the selected language', () => {
- const push = jest.fn();
- const router = mockRouter({ push });
- const wrapper = shallowRender({ router });
- expect(wrapper.find(CategoryDefinitionsList).props().category).toBe('java');
-
- const { onChange } = wrapper.find(Select).props();
-
- onChange({ label: '', originalValue: 'CoBoL', value: 'cobol' });
- expect(push).toHaveBeenCalledWith(expect.objectContaining({ query: { category: 'CoBoL' } }));
-});
-
-it('should correctly show the subcategory for a component', () => {
- const component = mockComponent();
- const wrapper = shallowRender({ component });
- expect(wrapper.find(CategoryDefinitionsList).props().component).toBe(component);
-});
-
-function shallowRender(props: Partial<LanguagesProps> = {}) {
- return shallow(
- <Languages
- categories={['Java', 'JavaScript', 'COBOL']}
- component={undefined}
- definitions={[]}
- location={mockLocation()}
- router={mockRouter()}
- selectedCategory="java"
- {...props}
- />
- );
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { 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<NewCodePeriod>(<NewCodePeriod />);
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { 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<NewCodePeriod>(<NewCodePeriod />);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import PageHeader, { PageHeaderProps } from '../PageHeader';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('global');
- expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('for project');
-});
-
-function shallowRender(props: Partial<PageHeaderProps> = {}) {
- return shallow<PageHeaderProps>(<PageHeader definitions={[]} {...props} />);
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import React from 'react';
+import { Route } from 'react-router-dom';
+import selectEvent from 'react-select-event';
+import { byRole } from 'testing-library-selector';
+import SettingsServiceMock from '../../../../api/mocks/SettingsServiceMock';
+import { KeyboardKeys } from '../../../../helpers/keycodes';
+import { mockComponent } from '../../../../helpers/mocks/component';
+import {
+ renderAppRoutes,
+ renderAppWithComponentContext,
+ RenderContext,
+} from '../../../../helpers/testReactTestingUtils';
+import { Feature } from '../../../../types/features';
+import { Component } from '../../../../types/types';
+import routes from '../../routes';
+
+jest.mock('../../../../api/settings');
+
+let settingsMock: SettingsServiceMock;
+
+beforeAll(() => {
+ settingsMock = new SettingsServiceMock();
+});
+
+afterEach(() => {
+ settingsMock.reset();
+});
+
+beforeEach(jest.clearAllMocks);
+
+const ui = {
+ categoryLink: (category: string) => byRole('link', { name: category }),
+ announcementHeading: byRole('heading', { name: 'property.category.general.Announcement' }),
+
+ languagesHeading: byRole('heading', { name: 'property.category.languages' }),
+ languagesSelect: byRole('combobox', { name: 'property.category.languages' }),
+ jsGeneralSubCategoryHeading: byRole('heading', { name: 'property.category.javascript.General' }),
+
+ settingsSearchInput: byRole('searchbox', { name: 'settings.search.placeholder' }),
+ searchList: byRole('list', { name: 'settings.search.results' }),
+ searchItem: (key: string) => byRole('link', { name: new RegExp(key) }),
+ searchClear: byRole('button', { name: 'clear' }),
+
+ externalAnalyzersAndroidHeading: byRole('heading', {
+ name: 'property.category.External Analyzers.Android',
+ }),
+ generalComputeEngineHeading: byRole('heading', {
+ name: 'property.category.general.Compute Engine',
+ }),
+};
+
+describe('Global Settings', () => {
+ it('renders categories list and definitions', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp();
+
+ const globalCategories = [
+ 'property.category.general',
+ 'property.category.languages',
+ 'property.category.External Analyzers',
+ 'settings.new_code_period.category',
+ 'property.category.almintegration',
+ ];
+
+ expect(await ui.categoryLink(globalCategories[0]).find()).toBeInTheDocument();
+ globalCategories.forEach((name) => {
+ expect(ui.categoryLink(name).get()).toBeInTheDocument();
+ });
+
+ expect(await ui.announcementHeading.find()).toBeInTheDocument();
+
+ // Navigating to Languages category
+ await user.click(await ui.categoryLink('property.category.languages').find());
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ });
+
+ it('renders Language category and can select any language', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp();
+
+ // Navigating to Languages category
+ await user.click(await ui.categoryLink('property.category.languages').find());
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+
+ await selectEvent.select(ui.languagesSelect.get(), 'property.category.javascript');
+ expect(await ui.jsGeneralSubCategoryHeading.find()).toBeInTheDocument();
+ });
+
+ it('can search definitions by name or key', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp();
+
+ expect(await ui.settingsSearchInput.find()).toBeInTheDocument();
+
+ // List popup should be closed if input is empty
+ await user.click(ui.settingsSearchInput.get());
+ expect(ui.searchList.query()).not.toBeInTheDocument();
+
+ // Should shot 'no results' based on input value
+ await user.type(ui.settingsSearchInput.get(), 'asdjasnd');
+ expect(ui.searchList.get()).toBeInTheDocument();
+ expect(within(ui.searchList.get()).getByText('no_results')).toBeInTheDocument();
+ await user.click(ui.searchClear.get());
+
+ // Should show results based on input value
+ const searchResultsKeys = [
+ 'sonar.announcement.message',
+ 'sonar.ce.parallelProjectTasks',
+ 'sonar.androidLint.reportPaths',
+ ];
+
+ await user.type(ui.settingsSearchInput.get(), 'an');
+ searchResultsKeys.forEach((key) => expect(ui.searchItem(key).get()).toBeInTheDocument());
+ expect(ui.searchItem('sonar.javascript.globals').query()).not.toBeInTheDocument();
+
+ // Navigating through keyboard
+ await user.keyboard(`{${KeyboardKeys.DownArrow}}`);
+ await user.keyboard(`{${KeyboardKeys.UpArrow}}`);
+ await user.keyboard(`{${KeyboardKeys.DownArrow}}`);
+ await user.keyboard(`{${KeyboardKeys.Enter}}`);
+
+ expect(await ui.externalAnalyzersAndroidHeading.find()).toBeInTheDocument();
+
+ // Navigating through link
+ await user.click(ui.searchClear.get());
+ await user.type(ui.settingsSearchInput.get(), 'an');
+ await user.click(ui.searchItem(searchResultsKeys[1]).get());
+
+ expect(await ui.generalComputeEngineHeading.find()).toBeInTheDocument();
+ });
+});
+
+describe('Project Settings', () => {
+ it('renders categories list and definitions', async () => {
+ const user = userEvent.setup();
+ renderSettingsApp(mockComponent(), { featureList: [Feature.BranchSupport] });
+
+ const projectCategories = [
+ 'property.category.general',
+ 'property.category.languages',
+ 'property.category.External Analyzers',
+ 'settings.pr_decoration.binding.category',
+ ];
+
+ expect(await ui.categoryLink(projectCategories[0]).find()).toBeInTheDocument();
+ projectCategories.forEach((name) => {
+ expect(ui.categoryLink(name).get()).toBeInTheDocument();
+ });
+
+ expect(await ui.announcementHeading.find()).toBeInTheDocument();
+
+ // Navigating to Languages category
+ await user.click(await ui.categoryLink('property.category.languages').find());
+ expect(await ui.languagesHeading.find()).toBeInTheDocument();
+ });
+});
+
+function renderSettingsApp(component?: Component, context: RenderContext = {}) {
+ const path = component ? 'project' : 'admin';
+ const wrapperRoutes = () => <Route path={path}>{routes()}</Route>;
+ const params: [string, typeof routes, RenderContext] = [
+ `${path}/settings`,
+ wrapperRoutes,
+ context,
+ ];
+
+ return component
+ ? renderAppWithComponentContext(...params, { component })
+ : renderAppRoutes(...params);
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { getDefinitions } from '../../../../api/settings';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import {
- addSideBarClass,
- addWhitePageClass,
- removeSideBarClass,
- removeWhitePageClass,
-} from '../../../../helpers/pages';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { SettingsApp } from '../SettingsApp';
-
-jest.mock('../../../../helpers/pages', () => ({
- addSideBarClass: jest.fn(),
- addWhitePageClass: jest.fn(),
- removeSideBarClass: jest.fn(),
- removeWhitePageClass: jest.fn(),
-}));
-
-jest.mock('../../../../api/settings', () => ({
- getDefinitions: jest.fn().mockResolvedValue([]),
-}));
-
-it('should render default view correctly', async () => {
- const wrapper = shallowRender();
-
- expect(addSideBarClass).toHaveBeenCalled();
- expect(addWhitePageClass).toHaveBeenCalled();
-
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-
- expect(getDefinitions).toHaveBeenCalledWith(undefined);
-
- wrapper.unmount();
-
- expect(removeSideBarClass).toHaveBeenCalled();
- expect(removeWhitePageClass).toHaveBeenCalled();
-});
-
-it('should fetch definitions for component', async () => {
- const key = 'component-key';
- const wrapper = shallowRender({ component: mockComponent({ key }) });
-
- await waitAndUpdate(wrapper);
- expect(getDefinitions).toHaveBeenCalledWith(key);
-});
-
-function shallowRender(props: Partial<SettingsApp['props']> = {}) {
- return shallow(<SettingsApp {...props} />);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper';
-import { mockDefinition } from '../../../../helpers/mocks/settings';
-import { mockLocation } from '../../../../helpers/testMocks';
-import {
- ALM_INTEGRATION_CATEGORY,
- ANALYSIS_SCOPE_CATEGORY,
- LANGUAGES_CATEGORY,
- NEW_CODE_PERIOD_CATEGORY,
- PULL_REQUEST_DECORATION_BINDING_CATEGORY,
-} from '../../constants';
-import { SettingsAppRenderer, SettingsAppRendererProps } from '../SettingsAppRenderer';
-
-it('should render loading correctly', () => {
- expect(shallowRender({ loading: true }).type()).toBeNull();
-});
-
-it('should render default view correctly', () => {
- const wrapper = shallowRender();
-
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find(ScreenPositionHelper).dive()).toMatchSnapshot('All Categories List');
-});
-
-it.each([
- [NEW_CODE_PERIOD_CATEGORY],
- [LANGUAGES_CATEGORY],
- [ANALYSIS_SCOPE_CATEGORY],
- [ALM_INTEGRATION_CATEGORY],
- [PULL_REQUEST_DECORATION_BINDING_CATEGORY],
-])('should render %s correctly', (category) => {
- const wrapper = shallowRender({
- location: mockLocation({ query: { category } }),
- });
-
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<SettingsAppRendererProps> = {}) {
- const definitions = [mockDefinition(), mockDefinition({ key: 'bar', category: 'general' })];
- return shallow(
- <SettingsAppRenderer
- definitions={definitions}
- loading={false}
- location={mockLocation()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { KeyboardKeys } from '../../../../helpers/keycodes';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockDefinition } from '../../../../helpers/mocks/settings';
-import { mockRouter } from '../../../../helpers/testMocks';
-import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils';
-import { queryToSearch } from '../../../../helpers/urls';
-import { SettingsSearch } from '../SettingsSearch';
-
-jest.mock('lunr', () =>
- jest.fn(() => ({
- search: jest.fn(() => [
- {
- ref: 'foo',
- },
- {
- ref: 'sonar.new_code_period',
- },
- ]),
- }))
-);
-
-describe('instance', () => {
- const router = mockRouter();
- const wrapper = shallowRender({ router });
-
- it('should build the index', () => {
- expect(wrapper.instance().index).not.toBeNull();
-
- const def = mockDefinition();
- expect(wrapper.instance().definitionsByKey).toEqual(
- expect.objectContaining({ [def.key]: def })
- );
- });
-
- it('should handle search', async () => {
- wrapper.instance().handleSearchChange('query');
-
- await waitAndUpdate(wrapper);
- expect(wrapper.state().searchQuery).toBe('query');
-
- expect(wrapper.instance().index.search).toHaveBeenCalled();
- expect(wrapper.state().showResults).toBe(true);
- expect(wrapper.state().results).toHaveLength(2);
- });
-
- it('should handle empty search', async () => {
- wrapper.instance().handleSearchChange('');
-
- await waitAndUpdate(wrapper);
- expect(wrapper.state().searchQuery).toBe('');
-
- expect(wrapper.instance().index.search).toHaveBeenCalled();
- expect(wrapper.state().showResults).toBe(false);
- });
-
- it('should hide results', () => {
- wrapper.setState({ showResults: true });
- wrapper.instance().hideResults();
- expect(wrapper.state().showResults).toBe(false);
- wrapper.instance().hideResults();
- });
-
- it('should handle focus', () => {
- wrapper.setState({ searchQuery: 'hi', showResults: false });
- wrapper.instance().handleFocus();
- expect(wrapper.state().showResults).toBe(true);
-
- wrapper.setState({ searchQuery: '', showResults: false });
- wrapper.instance().handleFocus();
- expect(wrapper.state().showResults).toBe(false);
- });
-
- it('should handle mouseover', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleMouseOverResult('selection');
- expect(wrapper.state().selectedResult).toBe('selection');
- });
-
- it('should handle "enter" keyboard event', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.Enter } }));
- expect(router.push).not.toHaveBeenCalled();
-
- wrapper.setState({ selectedResult: 'foo' });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.Enter } }));
-
- expect(router.push).toHaveBeenCalledWith({
- hash: '#foo',
- pathname: '/admin/settings',
- search: queryToSearch({ category: 'foo category' }),
- });
- });
-
- it('should handle "down" keyboard event', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.DownArrow } }));
- expect(wrapper.state().selectedResult).toBeUndefined();
-
- wrapper.setState({ selectedResult: 'foo' });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.DownArrow } }));
- expect(wrapper.state().selectedResult).toBe('sonar.new_code_period');
-
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.DownArrow } }));
- expect(wrapper.state().selectedResult).toBe('sonar.new_code_period');
- });
-
- it('should handle "up" keyboard event', () => {
- wrapper.setState({ selectedResult: undefined });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.UpArrow } }));
- expect(wrapper.state().selectedResult).toBeUndefined();
-
- wrapper.setState({ selectedResult: 'sonar.new_code_period' });
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.UpArrow } }));
- expect(wrapper.state().selectedResult).toBe('foo');
-
- wrapper.instance().handleKeyDown(mockEvent({ nativeEvent: { key: KeyboardKeys.UpArrow } }));
- expect(wrapper.state().selectedResult).toBe('foo');
- });
-});
-
-describe('project settings search', () => {
- it('should load the correct definitions', () => {
- const wrapper = shallowRender({ component: mockComponent(), definitions: [] });
-
- expect(Object.keys(wrapper.instance().definitionsByKey)).toHaveLength(1);
- });
-});
-
-function shallowRender(overrides: Partial<SettingsSearch['props']> = {}) {
- return shallow<SettingsSearch>(
- <SettingsSearch definitions={[mockDefinition()]} router={mockRouter()} {...overrides} />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockDefinition } from '../../../../helpers/mocks/settings';
-import { scrollToElement } from '../../../../helpers/scrolling';
-import SettingsSearchRenderer, { SettingsSearchRendererProps } from '../SettingsSearchRenderer';
-
-jest.mock('../../../../helpers/scrolling', () => ({
- scrollToElement: jest.fn(),
-}));
-
-jest.mock('react', () => {
- return {
- ...jest.requireActual('react'),
- useRef: jest.fn(),
- useEffect: jest.fn(),
- };
-});
-
-afterAll(() => {
- jest.clearAllMocks();
-});
-
-it('should render correctly when closed', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render correctly when open', () => {
- expect(shallowRender({ showResults: true })).toMatchSnapshot('no results');
- expect(
- shallowRender({
- results: [mockDefinition({ name: 'Foo!' }), mockDefinition({ key: 'bar' })],
- selectedResult: 'bar',
- showResults: true,
- })
- ).toMatchSnapshot('results');
-});
-
-it('should scroll to selected element', () => {
- const scrollable = {};
- const scrollableRef = { current: scrollable };
- const selected = {};
- const selectedRef = { current: selected };
-
- (React.useRef as jest.Mock)
- .mockImplementationOnce(() => scrollableRef)
- .mockImplementationOnce(() => selectedRef);
- (React.useEffect as jest.Mock).mockImplementationOnce((f) => f());
-
- shallowRender();
-
- expect(scrollToElement).toHaveBeenCalled();
-});
-
-function shallowRender(overrides: Partial<SettingsSearchRendererProps> = {}) {
- return shallow<SettingsSearchRendererProps>(
- <SettingsSearchRenderer
- searchQuery=""
- showResults={false}
- onClickOutside={jest.fn()}
- onMouseOverResult={jest.fn()}
- onSearchInputChange={jest.fn()}
- onSearchInputFocus={jest.fn()}
- onSearchInputKeyDown={jest.fn()}
- {...overrides}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { mount, shallow } from 'enzyme';
-import * as React from 'react';
-import { mockSettingWithCategory } from '../../../../helpers/mocks/settings';
-import { mockLocation } from '../../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import {
- SubCategoryDefinitionsList,
- SubCategoryDefinitionsListProps,
-} from '../SubCategoryDefinitionsList';
-
-jest.mock('../../../../helpers/scrolling', () => ({
- scrollToElement: jest.fn(),
-}));
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('');
- expect(shallowRender({ subCategory: 'qg' })).toMatchSnapshot('subcategory');
-});
-
-it('should scroll if hash is defined and updated', async () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn();
- const wrapper = shallowRender({ location: mockLocation({ hash: '#qg' }) });
-
- await waitAndUpdate(wrapper);
-
- wrapper.find('h2').forEach((node) => mount(node.getElement()));
-
- expect(window.HTMLElement.prototype.scrollIntoView).toHaveBeenCalled();
-
- wrapper.setProps({ location: mockLocation({ hash: '#email' }) });
-
- expect(window.HTMLElement.prototype.scrollIntoView).toHaveBeenCalled();
-});
-
-function shallowRender(props: Partial<SubCategoryDefinitionsListProps> = {}) {
- return shallow<SubCategoryDefinitionsListProps>(
- <SubCategoryDefinitionsList
- category="general"
- location={mockLocation()}
- settings={[
- mockSettingWithCategory(),
- mockSettingWithCategory({
- definition: {
- key: 'qg',
- category: 'general',
- subCategory: 'qg',
- fields: [],
- options: [],
- description: 'awesome description',
- },
- }),
- ]}
- {...props}
- />
- );
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render additional categories component correctly 1`] = `
-<withRouter(Languages)
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
-
-exports[`should render additional categories component correctly 2`] = `<NewCodePeriod />`;
-
-exports[`should render additional categories component correctly 3`] = `
-<AnalysisScope
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
-
-exports[`should render additional categories component correctly 4`] = `
-<withRouter(withAvailableFeaturesContext(AlmIntegration))
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
-
-exports[`should render additional categories component correctly 5`] = `
-<withCurrentUserContext(PRDecorationBinding)
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
-/>
-`;
-
-exports[`should render additional categories component correctly 6`] = `
-<withAvailableFeaturesContext(Authentication)
- categories={[]}
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- selectedCategory="TEST"
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
- <p
- className="spacer-bottom"
- >
- settings.analysis_scope.wildcards.introduction
- <withAppStateContext(DocLink)
- className="spacer-left"
- to="/project-administration/narrowing-the-focus/"
- >
- learn_more
- </withAppStateContext(DocLink)>
- </p>
- <table
- className="data spacer-bottom"
- >
- <tbody>
- <tr>
- <td>
- *
- </td>
- <td>
- settings.analysis_scope.wildcards.zero_more_char
- </td>
- </tr>
- <tr>
- <td>
- **
- </td>
- <td>
- settings.analysis_scope.wildcards.zero_more_dir
- </td>
- </tr>
- <tr>
- <td>
- ?
- </td>
- <td>
- settings.analysis_scope.wildcards.single_char
- </td>
- </tr>
- </tbody>
- </table>
- <div
- className="settings-sub-category"
- >
- <CategoryDefinitionsList
- category="TEST"
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- />
- </div>
-</Fragment>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`disables save button on error 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={true}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
-
-exports[`displays cancel button when value changed and has error 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={true}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
-
-exports[`displays cancel button when value changed and no error 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={false}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
-
-exports[`displays default message when value is default 1`] = `
-<Fragment>
- <div
- className="spacer-top note"
- style={
- {
- "lineHeight": "24px",
- }
- }
- >
- settings._default
- </div>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right"
- onClick={[Function]}
- >
- reset_verb
- </Button>
- <span
- className="note"
- >
- default
- :
- settings.default.no_value
- </span>
- </div>
-</Fragment>
-`;
-
-exports[`displays reset button when empty and not default 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right"
- onClick={[Function]}
- >
- reset_verb
- </Button>
- <span
- className="note"
- >
- default
- :
- settings.default.no_value
- </span>
- </div>
-</Fragment>
-`;
-
-exports[`displays save button when it can be saved 1`] = `
-<Fragment>
- <div
- className="settings-definition-changes nowrap"
- >
- <Button
- className="spacer-right button-success"
- disabled={false}
- onClick={[MockFunction]}
- >
- save
- </Button>
- <ResetButtonLink
- className="spacer-right"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
-</Fragment>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: changed value 1`] = `
-<div
- className="settings-definition settings-definition-changed"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- />
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={true}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- value="new value"
- />
- <DefinitionActions
- changedValue="new value"
- hasError={false}
- hasValueChanged={true}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: in error 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- >
- <span
- className="text-danger"
- >
- <AlertErrorIcon
- className="spacer-right"
- />
- <span>
- settings.state.validation_failed.validation message
- </span>
- </span>
- </div>
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={true}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: loading 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- >
- <span
- className="text-info"
- >
- <i
- className="spinner spacer-right"
- />
- settings.state.saving
- </span>
- </div>
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: original value 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- />
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": true,
- "key": "foo",
- "value": "original value",
- }
- }
- value="original value"
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": true,
- "key": "foo",
- "value": "original value",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: success 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- >
- <span
- className="text-success"
- >
- <AlertSuccessIcon
- className="spacer-right"
- />
- settings.state.saved
- </span>
- </div>
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: with description 1`] = `
-<div
- className="settings-definition"
- data-key="foo"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="property.foo.name"
- >
- property.foo.name
- </h3>
- <div
- className="markdown small spacer-top"
- dangerouslySetInnerHTML={
- {
- "__html": "property.foo.description",
- }
- }
- />
- <Tooltip
- overlay="settings.key_x.foo"
- >
- <div
- className="settings-definition-key note little-spacer-top"
- >
- settings.key_x.foo
- </div>
- </Tooltip>
- </div>
- <div
- className="settings-definition-right"
- >
- <div
- className="settings-definition-state"
- />
- <form
- onSubmit={[Function]}
- >
- <Input
- hasValueChanged={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onChange={[MockFunction]}
- onEditing={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "description": "description",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- <DefinitionActions
- hasError={false}
- hasValueChanged={false}
- isDefault={false}
- isEditing={false}
- onCancel={[MockFunction]}
- onReset={[MockFunction]}
- onSave={[MockFunction]}
- setting={
- {
- "definition": {
- "category": "foo category",
- "description": "description",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- "hasValue": false,
- "key": "foo",
- }
- }
- />
- </form>
- </div>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={false}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={false}
- >
- email_configuration.test.send
- </SubmitButton>
- </form>
-</div>
-`;
-
-exports[`should render correctly: error 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <div
- className="form-field"
- >
- <Alert
- variant="error"
- >
- Some error message
- </Alert>
- </div>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={false}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={false}
- >
- email_configuration.test.send
- </SubmitButton>
- </form>
-</div>
-`;
-
-exports[`should render correctly: sending 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={true}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={true}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={true}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={true}
- >
- email_configuration.test.send
- </SubmitButton>
- <DeferredSpinner
- className="spacer-left"
- />
- </form>
-</div>
-`;
-
-exports[`should render correctly: success 1`] = `
-<div
- className="settings-definition"
->
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- >
- email_configuration.test.title
- </h3>
- </div>
- <form
- className="settings-definition-right"
- onSubmit={[Function]}
- >
- <div
- className="form-field"
- >
- <Alert
- variant="success"
- >
- email_configuration.test.email_was_sent_to_x.email@example.com
- </Alert>
- </div>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-to"
- >
- email_configuration.test.to_address
- <MandatoryFieldMarker />
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-to"
- onChange={[Function]}
- required={true}
- type="email"
- value="luke@skywalker.biz"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-subject"
- >
- email_configuration.test.subject
- </label>
- <input
- className="settings-large-input"
- disabled={false}
- id="test-email-subject"
- onChange={[Function]}
- type="text"
- value="email_configuration.test.subject"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="test-email-message"
- >
- email_configuration.test.message
- <MandatoryFieldMarker />
- </label>
- <textarea
- className="settings-large-input"
- disabled={false}
- id="test-email-message"
- onChange={[Function]}
- required={true}
- rows={5}
- value="email_configuration.test.message_text"
- />
- </div>
- <SubmitButton
- disabled={false}
- >
- email_configuration.test.send
- </SubmitButton>
- </form>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
- <h2
- className="settings-sub-category-name"
- id="languages-category-title"
- >
- property.category.languages
- </h2>
- <div
- data-test="language-select"
- >
- <Select
- aria-labelledby="languages-category-title"
- className="input-large select-settings-language"
- onChange={[Function]}
- options={
- [
- {
- "label": "property.category.Java",
- "originalValue": "Java",
- "value": "java",
- },
- {
- "label": "property.category.JavaScript",
- "originalValue": "JavaScript",
- "value": "javascript",
- },
- {
- "label": "property.category.COBOL",
- "originalValue": "COBOL",
- "value": "cobol",
- },
- ]
- }
- placeholder="settings.languages.select_a_language_placeholder"
- value={
- {
- "label": "property.category.Java",
- "originalValue": "Java",
- "value": "java",
- }
- }
- />
- </div>
- <div
- className="settings-sub-category"
- >
- <CategoryDefinitionsList
- category="java"
- definitions={[]}
- />
- </div>
-</Fragment>
-`;
-
-exports[`should render correctly with an unknow language 1`] = `
-<Fragment>
- <h2
- className="settings-sub-category-name"
- id="languages-category-title"
- >
- property.category.languages
- </h2>
- <div
- data-test="language-select"
- >
- <Select
- aria-labelledby="languages-category-title"
- className="input-large select-settings-language"
- onChange={[Function]}
- options={
- [
- {
- "label": "property.category.Java",
- "originalValue": "Java",
- "value": "java",
- },
- {
- "label": "property.category.JavaScript",
- "originalValue": "JavaScript",
- "value": "javascript",
- },
- {
- "label": "property.category.COBOL",
- "originalValue": "COBOL",
- "value": "cobol",
- },
- ]
- }
- placeholder="settings.languages.select_a_language_placeholder"
- />
- </div>
-</Fragment>
-`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<ul
+ className="settings-sub-categories-list"
+>
+ <li>
+ <ul
+ className="settings-definitions-list"
+ >
+ <li>
+ <div
+ className="settings-definition"
+ >
+ <div
+ className="settings-definition-left"
+ >
+ <h3
+ className="settings-definition-name"
+ title="settings.new_code_period.title"
+ >
+ settings.new_code_period.title
+ </h3>
+ <div
+ className="small big-spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.new_code_period.description"
+ id="settings.new_code_period.description"
+ values={
+ {
+ "link": <withAppStateContext(DocLink)
+ to="/project-administration/defining-new-code/"
+ >
+ learn_more
+ </withAppStateContext(DocLink)>,
+ }
+ }
+ />
+ <p
+ className="spacer-top"
+ >
+ settings.new_code_period.description2
+ </p>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right"
+ >
+ <DeferredSpinner />
+ </div>
+ </div>
+ </li>
+ </ul>
+ </li>
+</ul>
+`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ul
- className="settings-sub-categories-list"
->
- <li>
- <ul
- className="settings-definitions-list"
- >
- <li>
- <div
- className="settings-definition"
- >
- <div
- className="settings-definition-left"
- >
- <h3
- className="settings-definition-name"
- title="settings.new_code_period.title"
- >
- settings.new_code_period.title
- </h3>
- <div
- className="small big-spacer-top"
- >
- <FormattedMessage
- defaultMessage="settings.new_code_period.description"
- id="settings.new_code_period.description"
- values={
- {
- "link": <withAppStateContext(DocLink)
- to="/project-administration/defining-new-code/"
- >
- learn_more
- </withAppStateContext(DocLink)>,
- }
- }
- />
- <p
- className="spacer-top"
- >
- settings.new_code_period.description2
- </p>
- </div>
- </div>
- <div
- className="settings-definition-right"
- >
- <DeferredSpinner />
- </div>
- </div>
- </li>
- </ul>
- </li>
-</ul>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: for project 1`] = `
-<header
- className="top-bar-outer"
->
- <div
- className="top-bar"
- >
- <div
- className="top-bar-inner bordered-bottom big-padded-top padded-bottom"
- >
- <h1
- className="page-title"
- >
- project_settings.page
- </h1>
- <div
- className="page-description spacer-top"
- >
- project_settings.page.description
- </div>
- <withRouter(SettingsSearch)
- className="big-spacer-top"
- component={
- {
- "breadcrumbs": [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": [
- {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": [],
- }
- }
- definitions={[]}
- />
- </div>
- </div>
-</header>
-`;
-
-exports[`should render correctly: global 1`] = `
-<header
- className="top-bar-outer"
->
- <div
- className="top-bar"
- >
- <div
- className="top-bar-inner bordered-bottom big-padded-top padded-bottom"
- >
- <h1
- className="page-title"
- >
- settings.page
- </h1>
- <div
- className="page-description spacer-top"
- >
- <InstanceMessage
- message="settings.page.description"
- />
- </div>
- <withRouter(SettingsSearch)
- className="big-spacer-top"
- definitions={[]}
- />
- </div>
- </div>
-</header>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render default view correctly 1`] = `
-<withRouter(SettingsAppRenderer)
- definitions={[]}
- loading={false}
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render almintegration correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="almintegration"
- >
- <withRouter(withAvailableFeaturesContext(AlmIntegration))
- categories={
- [
- "foo category",
- "general",
- ]
- }
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- selectedCategory="almintegration"
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render default view correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="general"
- >
- <CategoryDefinitionsList
- category="general"
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render default view correctly: All Categories List 1`] = `
-<div
- className="layout-page-side-outer"
->
- <div
- className="layout-page-side"
- style={
- {
- "top": 0,
- }
- }
- >
- <div
- className="layout-page-side-inner"
- >
- <withAvailableFeaturesContext(CategoriesList)
- categories={
- [
- "foo category",
- "general",
- ]
- }
- defaultCategory="general"
- selectedCategory="general"
- />
- </div>
- </div>
-</div>
-`;
-
-exports[`should render exclusions correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="exclusions"
- >
- <AnalysisScope
- categories={
- [
- "foo category",
- "general",
- ]
- }
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- selectedCategory="exclusions"
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render languages correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="languages"
- >
- <withRouter(Languages)
- categories={
- [
- "foo category",
- "general",
- ]
- }
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- selectedCategory="languages"
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render new_code_period correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="new_code_period"
- >
- <NewCodePeriod />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
-
-exports[`should render pull_request_decoration_binding correctly 1`] = `
-<main
- id="settings-page"
->
- <Suggestions
- suggestions="settings"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="settings.page"
- />
- <PageHeader
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- <div
- className="layout-page"
- >
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <div
- className="layout-page-main-inner"
- >
- <div
- className="big-padded"
- key="pull_request_decoration_binding"
- >
- <CategoryDefinitionsList
- category="pull_request_decoration_binding"
- definitions={
- [
- {
- "category": "foo category",
- "fields": [],
- "key": "foo",
- "options": [],
- "subCategory": "foo subCat",
- },
- {
- "category": "general",
- "fields": [],
- "key": "bar",
- "options": [],
- "subCategory": "foo subCat",
- },
- ]
- }
- />
- </div>
- </div>
- </div>
- </div>
-</main>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly when closed 1`] = `
-<OutsideClickHandler
- onClickOutside={[MockFunction]}
->
- <div
- className="dropdown"
- >
- <SearchBox
- onChange={[MockFunction]}
- onFocus={[MockFunction]}
- onKeyDown={[MockFunction]}
- placeholder="settings.search.placeholder"
- value=""
- />
- </div>
-</OutsideClickHandler>
-`;
-
-exports[`should render correctly when open: no results 1`] = `
-<OutsideClickHandler
- onClickOutside={[MockFunction]}
->
- <div
- className="dropdown"
- >
- <SearchBox
- onChange={[MockFunction]}
- onFocus={[MockFunction]}
- onKeyDown={[MockFunction]}
- placeholder="settings.search.placeholder"
- value=""
- />
- <DropdownOverlay
- noPadding={true}
- >
- <ul
- className="settings-search-results menu"
- >
- <div
- className="big-padded"
- >
- no_results
- </div>
- </ul>
- </DropdownOverlay>
- </div>
-</OutsideClickHandler>
-`;
-
-exports[`should render correctly when open: results 1`] = `
-<OutsideClickHandler
- onClickOutside={[MockFunction]}
->
- <div
- className="dropdown"
- >
- <SearchBox
- onChange={[MockFunction]}
- onFocus={[MockFunction]}
- onKeyDown={[MockFunction]}
- placeholder="settings.search.placeholder"
- value=""
- />
- <DropdownOverlay
- noPadding={true}
- >
- <ul
- className="settings-search-results menu"
- >
- <li
- className="spacer-bottom spacer-top"
- key="foo"
- >
- <ForwardRef(Link)
- onClick={[MockFunction]}
- onMouseEnter={[Function]}
- to={
- {
- "hash": "#foo",
- "pathname": "/admin/settings",
- "search": "?category=foo+category",
- }
- }
- >
- <div
- className="settings-search-result-title display-flex-space-between"
- >
- <h3>
- Foo!
- </h3>
- </div>
- <div
- className="note spacer-top"
- >
- settings.key_x.foo
- </div>
- </ForwardRef(Link)>
- </li>
- <li
- className="spacer-bottom spacer-top active"
- key="bar"
- >
- <ForwardRef(Link)
- onClick={[MockFunction]}
- onMouseEnter={[Function]}
- to={
- {
- "hash": "#bar",
- "pathname": "/admin/settings",
- "search": "?category=foo+category",
- }
- }
- >
- <div
- className="settings-search-result-title display-flex-space-between"
- >
- <h3>
- foo subCat
- </h3>
- </div>
- <div
- className="note spacer-top"
- >
- settings.key_x.bar
- </div>
- </ForwardRef(Link)>
- </li>
- </ul>
- </DropdownOverlay>
- </div>
-</OutsideClickHandler>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ul
- className="settings-sub-categories-list"
->
- <li
- key="email"
- >
- <h2
- className="settings-sub-category-name"
- data-key="email"
- >
- property.category.general.email
- </h2>
- <div
- className="settings-sub-category-description markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "property.category.general.email.description",
- }
- }
- />
- <DefinitionsList
- scrollToDefinition={[Function]}
- settings={
- [
- {
- "definition": {
- "category": "general",
- "description": "When Foo then Bar",
- "fields": [],
- "key": "foo",
- "name": "Foo setting",
- "options": [],
- "subCategory": "email",
- "type": "INTEGER",
- },
- "hasValue": true,
- "inherited": true,
- "key": "foo",
- "value": "42",
- },
- ]
- }
- />
- <withCurrentUserContext(EmailForm) />
- </li>
- <li
- key="qg"
- >
- <h2
- className="settings-sub-category-name"
- data-key="qg"
- >
- property.category.general.qg
- </h2>
- <div
- className="settings-sub-category-description markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "property.category.general.qg.description",
- }
- }
- />
- <DefinitionsList
- scrollToDefinition={[Function]}
- settings={
- [
- {
- "definition": {
- "category": "general",
- "description": "awesome description",
- "fields": [],
- "key": "qg",
- "options": [],
- "subCategory": "qg",
- },
- "hasValue": true,
- "inherited": true,
- "key": "foo",
- "value": "42",
- },
- ]
- }
- />
- </li>
-</ul>
-`;
-
-exports[`should render correctly: subcategory 1`] = `
-<ul
- className="settings-sub-categories-list"
->
- <li
- key="qg"
- >
- <h2
- className="settings-sub-category-name"
- data-key="qg"
- >
- property.category.general.qg
- </h2>
- <div
- className="settings-sub-category-description markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "property.category.general.qg.description",
- }
- }
- />
- <DefinitionsList
- scrollToDefinition={[Function]}
- settings={
- [
- {
- "definition": {
- "category": "general",
- "description": "awesome description",
- "fields": [],
- "key": "qg",
- "options": [],
- "subCategory": "qg",
- },
- "hasValue": true,
- "inherited": true,
- "key": "foo",
- "value": "42",
- },
- ]
- }
- />
- </li>
-</ul>
-`;
*/
import * as React from 'react';
import { DeleteButton } from '../../../../components/controls/buttons';
-import { DefaultSpecializedInputProps, getEmptyValue } from '../../utils';
+import { translateWithParameters } from '../../../../helpers/l10n';
+import { DefaultSpecializedInputProps, getEmptyValue, getPropertyName } from '../../utils';
import PrimitiveInput from './PrimitiveInput';
export default class MultiValueInput extends React.PureComponent<DefaultSpecializedInputProps> {
<div className="display-inline-block spacer-left">
<DeleteButton
className="js-remove-value"
+ aria-label={translateWithParameters(
+ 'settings.definition.delete_value',
+ getPropertyName(setting.definition),
+ value
+ )}
onClick={() => this.handleDeleteValue(index)}
/>
</div>
import classNames from 'classnames';
import * as React from 'react';
import { KeyboardKeys } from '../../../../helpers/keycodes';
-import { DefaultSpecializedInputProps } from '../../utils';
+import { DefaultSpecializedInputProps, getPropertyName } from '../../utils';
export interface SimpleInputProps extends DefaultSpecializedInputProps {
value: string | number;
};
render() {
- const { autoComplete, autoFocus, className, name, value = '', type } = this.props;
+ const { autoComplete, autoFocus, className, name, value = '', setting, type } = this.props;
return (
<input
autoComplete={autoComplete}
onKeyDown={this.handleKeyDown}
type={type}
value={value}
+ aria-label={getPropertyName(setting.definition)}
/>
);
}
overflow-x: hidden;
}
+.settings-search-results > li > a:hover {
+ background-color: unset;
+ border-left-color: unset;
+}
+
+.settings-search-results > li.active > a {
+ background-color: var(--neutral50);
+ border-left-color: var(--blacka60);
+}
+
.fixed-footer {
position: sticky;
bottom: 0px;
export function getPropertyName(definition: SettingDefinition) {
const key = `property.${definition.key}.name`;
- return hasMessage(key) ? translate(key) : definition.name;
+ if (hasMessage(key)) {
+ return translate(key);
+ }
+
+ return definition.name ?? definition.key;
}
export function getPropertyDescription(definition: SettingDefinition) {
settings.default.password=<password>
settings.reset_confirm.title=Reset Setting
settings.reset_confirm.description=Are you sure that you want to reset this setting?
+settings.definition.reset=Reset "{0}" to default values
+settings.definition.delete_value=Delete value "{1}" for setting "{0}"
settings.search.placeholder=Find in Settings
+settings.search.results=Search results list
settings.json.format=Format JSON
settings.json.format_error=Formatting requires valid JSON. Please fix it and retry.