--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { Button } from 'sonar-ui-common/components/controls/buttons';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { DefaultSpecializedInputProps } from '../../utils';
+
+const JSON_SPACE_SIZE = 4;
+
+interface State {
+ formatError: boolean;
+}
+
+export default class InputForJSON extends React.PureComponent<DefaultSpecializedInputProps, State> {
+ state: State = { formatError: false };
+
+ handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this.setState({ formatError: false });
+ this.props.onChange(event.target.value);
+ };
+
+ format = () => {
+ const { value } = this.props;
+ try {
+ if (value) {
+ this.props.onChange(JSON.stringify(JSON.parse(value), undefined, JSON_SPACE_SIZE));
+ }
+ } catch (e) {
+ this.setState({ formatError: true });
+ }
+ };
+
+ render() {
+ const { formatError } = this.state;
+ return (
+ <div className="display-flex-end">
+ <textarea
+ className="settings-large-input text-top monospaced spacer-right"
+ name={this.props.name}
+ onChange={this.handleInputChange}
+ rows={5}
+ value={this.props.value || ''}
+ />
+ <div>
+ {formatError && <Alert variant="info">{translate('settings.json.format_error')} </Alert>}
+ <Button className="spacer-top" onClick={this.format}>
+ {translate('settings.json.format')}
+ </Button>
+ </div>
+ </div>
+ );
+ }
+}
isDefaultOrInherited
} from '../../utils';
import InputForBoolean from './InputForBoolean';
+import InputForJSON from './InputForJSON';
import InputForNumber from './InputForNumber';
import InputForPassword from './InputForPassword';
import InputForSingleSelectList from './InputForSingleSelectList';
} = {
STRING: InputForString,
TEXT: InputForText,
- JSON: InputForText,
+ JSON: InputForJSON,
PASSWORD: InputForPassword,
BOOLEAN: InputForBoolean,
INTEGER: InputForNumber,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { change } from 'sonar-ui-common/helpers/testUtils';
+import { DefaultSpecializedInputProps } from '../../../utils';
+import InputForJSON from '../InputForJSON';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should call onChange', () => {
+ const onChange = jest.fn();
+ const wrapper = shallowRender({ onChange });
+
+ change(wrapper.find('textarea'), '{"a": 1}');
+ expect(onChange).toBeCalledWith('{"a": 1}');
+});
+
+it('should handle formatting for invalid JSON', () => {
+ const onChange = jest.fn();
+ const wrapper = shallowRender({ onChange, value: '{"a": 1b}' });
+ wrapper.instance().format();
+ expect(onChange).not.toBeCalled();
+
+ expect(wrapper.state().formatError).toBe(true);
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should handle formatting for valid JSON', () => {
+ const onChange = jest.fn();
+ const wrapper = shallowRender({ onChange, value: '{"a": 1}' });
+ wrapper.instance().format();
+ expect(onChange).toBeCalledWith(`{
+ "a": 1
+}`);
+
+ expect(wrapper.state().formatError).toBe(false);
+});
+
+it('should handle ignore formatting if empty', () => {
+ const onChange = jest.fn();
+ const wrapper = shallowRender({ onChange, value: '' });
+ wrapper.instance().format();
+ expect(onChange).not.toBeCalled();
+
+ expect(wrapper.state().formatError).toBe(false);
+});
+
+function shallowRender(props: Partial<DefaultSpecializedInputProps> = {}) {
+ return shallow<InputForJSON>(
+ <InputForJSON isDefault={false} name="foo" onChange={jest.fn()} value="" {...props} />
+ );
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should handle formatting for invalid JSON 1`] = `
+<div
+ className="display-flex-end"
+>
+ <textarea
+ className="settings-large-input text-top monospaced spacer-right"
+ name="foo"
+ onChange={[Function]}
+ rows={5}
+ value="{\\"a\\": 1b}"
+ />
+ <div>
+ <Alert
+ variant="info"
+ >
+ settings.json.format_error
+
+ </Alert>
+ <Button
+ className="spacer-top"
+ onClick={[Function]}
+ >
+ settings.json.format
+ </Button>
+ </div>
+</div>
+`;
+
+exports[`should render correctly 1`] = `
+<div
+ className="display-flex-end"
+>
+ <textarea
+ className="settings-large-input text-top monospaced spacer-right"
+ name="foo"
+ onChange={[Function]}
+ rows={5}
+ value=""
+ />
+ <div>
+ <Button
+ className="spacer-top"
+ onClick={[Function]}
+ >
+ settings.json.format
+ </Button>
+ </div>
+</div>
+`;
settings.reset_confirm.title=Reset Setting
settings.reset_confirm.description=Are you sure that you want to reset this setting?
+settings.json.format=Format JSON
+settings.json.format_error=Formatting requires valid JSON. Please fix it and retry.
+
settings.analysis_scope.wildcards.introduction=You can use the following wildcards.
settings.analysis_scope.wildcards.zero_more_char=Match zero or more characters
settings.analysis_scope.wildcards.zero_more_dir=Match zero or more directories