+++ /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 { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
-import Modal from 'sonar-ui-common/components/controls/Modal';
-import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker';
-import MandatoryFieldsExplanation from 'sonar-ui-common/components/ui/MandatoryFieldsExplanation';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { copyProfile } from '../../../api/quality-profiles';
-import { Profile } from '../types';
-
-interface Props {
- onClose: () => void;
- onCopy: (name: string) => void;
- profile: Profile;
-}
-
-interface State {
- loading: boolean;
- name: string | null;
-}
-
-export default class CopyProfileForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: false, name: null };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
- this.setState({ name: event.currentTarget.value });
- };
-
- handleFormSubmit = (event: React.SyntheticEvent<HTMLElement>) => {
- event.preventDefault();
-
- const { name } = this.state;
-
- if (name != null) {
- this.setState({ loading: true });
- copyProfile(this.props.profile.key, name).then(
- (profile: any) => this.props.onCopy(profile.name),
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- );
- }
- };
-
- render() {
- const { profile } = this.props;
- const header = translateWithParameters(
- 'quality_profiles.copy_x_title',
- profile.name,
- profile.languageName
- );
- const submitDisabled =
- this.state.loading || !this.state.name || this.state.name === profile.name;
-
- return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
- <form id="copy-profile-form" onSubmit={this.handleFormSubmit}>
- <div className="modal-head">
- <h2>{header}</h2>
- </div>
- <div className="modal-body">
- <MandatoryFieldsExplanation className="modal-field" />
- <div className="modal-field">
- <label htmlFor="copy-profile-name">
- {translate('quality_profiles.copy_new_name')}
- <MandatoryFieldMarker />
- </label>
- <input
- autoFocus={true}
- id="copy-profile-name"
- maxLength={100}
- name="name"
- onChange={this.handleNameChange}
- required={true}
- size={50}
- type="text"
- value={this.state.name != null ? this.state.name : profile.name}
- />
- </div>
- </div>
- <div className="modal-foot">
- {this.state.loading && <i className="spinner spacer-right" />}
- <SubmitButton disabled={submitDisabled} id="copy-profile-submit">
- {translate('copy')}
- </SubmitButton>
- <ResetButtonLink id="copy-profile-cancel" onClick={this.props.onClose}>
- {translate('cancel')}
- </ResetButtonLink>
- </div>
- </form>
- </Modal>
- );
- }
-}
import Modal from 'sonar-ui-common/components/controls/Modal';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { deleteProfile } from '../../../api/quality-profiles';
import { Profile } from '../types';
-interface Props {
+export interface DeleteProfileFormProps {
+ loading: boolean;
onClose: () => void;
onDelete: () => void;
profile: Profile;
}
-interface State {
- loading: boolean;
-}
-
-export default class DeleteProfileForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
- event.preventDefault();
- this.setState({ loading: true });
- deleteProfile(this.props.profile).then(this.props.onDelete, () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- });
- };
-
- render() {
- const { profile } = this.props;
- const header = translate('quality_profiles.delete_confirm_title');
+export default function DeleteProfileForm(props: DeleteProfileFormProps) {
+ const { loading, profile } = props;
+ const header = translate('quality_profiles.delete_confirm_title');
- return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
- <form id="delete-profile-form" onSubmit={this.handleFormSubmit}>
- <div className="modal-head">
- <h2>{header}</h2>
- </div>
- <div className="modal-body">
- <div className="js-modal-messages" />
- {profile.childrenCount > 0 ? (
- <div>
- <Alert variant="warning">
- {translate('quality_profiles.this_profile_has_descendants')}
- </Alert>
- <p>
- {translateWithParameters(
- 'quality_profiles.are_you_sure_want_delete_profile_x_and_descendants',
- profile.name,
- profile.languageName
- )}
- </p>
- </div>
- ) : (
+ return (
+ <Modal contentLabel={header} onRequestClose={props.onClose}>
+ <form
+ onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
+ e.preventDefault();
+ props.onDelete();
+ }}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ {profile.childrenCount > 0 ? (
+ <div>
+ <Alert variant="warning">
+ {translate('quality_profiles.this_profile_has_descendants')}
+ </Alert>
<p>
{translateWithParameters(
- 'quality_profiles.are_you_sure_want_delete_profile_x',
+ 'quality_profiles.are_you_sure_want_delete_profile_x_and_descendants',
profile.name,
profile.languageName
)}
</p>
- )}
- </div>
- <div className="modal-foot">
- {this.state.loading && <i className="spinner spacer-right" />}
- <SubmitButton
- className="button-red"
- disabled={this.state.loading}
- id="delete-profile-submit">
- {translate('delete')}
- </SubmitButton>
- <ResetButtonLink id="delete-profile-cancel" onClick={this.props.onClose}>
- {translate('cancel')}
- </ResetButtonLink>
- </div>
- </form>
- </Modal>
- );
- }
+ </div>
+ ) : (
+ <p>
+ {translateWithParameters(
+ 'quality_profiles.are_you_sure_want_delete_profile_x',
+ profile.name,
+ profile.languageName
+ )}
+ </p>
+ )}
+ </div>
+ <div className="modal-foot">
+ {loading && <i className="spinner spacer-right" />}
+ <SubmitButton className="button-red" disabled={loading}>
+ {translate('delete')}
+ </SubmitButton>
+ <ResetButtonLink onClick={props.onClose}>{translate('cancel')}</ResetButtonLink>
+ </div>
+ </form>
+ </Modal>
+ );
}
+++ /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 { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
-import Modal from 'sonar-ui-common/components/controls/Modal';
-import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
-import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker';
-import MandatoryFieldsExplanation from 'sonar-ui-common/components/ui/MandatoryFieldsExplanation';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { changeProfileParent, createQualityProfile } from '../../../api/quality-profiles';
-import { Profile } from '../types';
-
-interface Props {
- onClose: () => void;
- onExtend: (name: string) => void;
- profile: Profile;
-}
-
-interface State {
- loading: boolean;
- name?: string;
-}
-
-type ValidState = State & Required<Pick<State, 'name'>>;
-
-export default class ExtendProfileForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- canSubmit = (state: State): state is ValidState => {
- return Boolean(state.name && state.name.length);
- };
-
- handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
- this.setState({ name: event.currentTarget.value });
- };
-
- handleFormSubmit = async (event: React.SyntheticEvent<HTMLFormElement>) => {
- event.preventDefault();
- if (this.canSubmit(this.state)) {
- const { profile: parentProfile } = this.props;
- const { name } = this.state;
-
- const data = new FormData();
-
- data.append('language', parentProfile.language);
- data.append('name', name);
-
- this.setState({ loading: true });
-
- try {
- const { profile: newProfile } = await createQualityProfile(data);
- await changeProfileParent(newProfile, parentProfile);
- this.props.onExtend(newProfile.name);
- } finally {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- }
- };
-
- render() {
- const { profile } = this.props;
- const header = translateWithParameters(
- 'quality_profiles.extend_x_title',
- profile.name,
- profile.languageName
- );
-
- return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
- <form onSubmit={this.handleFormSubmit}>
- <div className="modal-head">
- <h2>{header}</h2>
- </div>
- <div className="modal-body">
- <MandatoryFieldsExplanation className="modal-field" />
- <div className="modal-field">
- <label htmlFor="extend-profile-name">
- {translate('quality_profiles.copy_new_name')}
- <MandatoryFieldMarker />
- </label>
- <input
- autoFocus={true}
- id="extend-profile-name"
- maxLength={100}
- name="name"
- onChange={this.handleNameChange}
- required={true}
- size={50}
- type="text"
- value={this.state.name ? this.state.name : ''}
- />
- </div>
- </div>
- <div className="modal-foot">
- <DeferredSpinner className="spacer-right" loading={this.state.loading} />
- <SubmitButton
- disabled={this.state.loading || !this.canSubmit(this.state)}
- id="extend-profile-submit">
- {translate('extend')}
- </SubmitButton>
- <ResetButtonLink id="extend-profile-cancel" onClick={this.props.onClose}>
- {translate('cancel')}
- </ResetButtonLink>
- </div>
- </form>
- </Modal>
- );
- }
-}
ActionsDropdownItem
} from 'sonar-ui-common/components/controls/ActionsDropdown';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { getQualityProfileBackupUrl, setDefaultProfile } from '../../../api/quality-profiles';
+import {
+ changeProfileParent,
+ copyProfile,
+ createQualityProfile,
+ deleteProfile,
+ getQualityProfileBackupUrl,
+ renameProfile,
+ setDefaultProfile
+} from '../../../api/quality-profiles';
import { Router, withRouter } from '../../../components/hoc/withRouter';
import { getBaseUrl } from '../../../helpers/system';
import { getRulesUrl } from '../../../helpers/urls';
-import { Profile } from '../types';
+import { Profile, ProfileActionModals } from '../types';
import { getProfileComparePath, getProfilePath, PROFILE_PATH } from '../utils';
-import CopyProfileForm from './CopyProfileForm';
import DeleteProfileForm from './DeleteProfileForm';
-import ExtendProfileForm from './ExtendProfileForm';
-import RenameProfileForm from './RenameProfileForm';
+import ProfileModalForm from './ProfileModalForm';
interface Props {
className?: string;
}
interface State {
- copyFormOpen: boolean;
- extendFormOpen: boolean;
- deleteFormOpen: boolean;
- renameFormOpen: boolean;
+ loading: boolean;
+ openModal?: ProfileActionModals;
}
export class ProfileActions extends React.PureComponent<Props, State> {
+ mounted = false;
state: State = {
- copyFormOpen: false,
- extendFormOpen: false,
- deleteFormOpen: false,
- renameFormOpen: false
+ loading: false
};
- closeCopyForm = () => {
- this.setState({ copyFormOpen: false });
- };
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
- closeDeleteForm = () => {
- this.setState({ deleteFormOpen: false });
+ handleCloseModal = () => {
+ this.setState({ openModal: undefined });
};
- closeExtendForm = () => {
- this.setState({ extendFormOpen: false });
+ handleCopyClick = () => {
+ this.setState({ openModal: ProfileActionModals.Copy });
};
- closeRenameForm = () => {
- this.setState({ renameFormOpen: false });
+ handleExtendClick = () => {
+ this.setState({ openModal: ProfileActionModals.Extend });
};
- handleCopyClick = () => {
- this.setState({ copyFormOpen: true });
+ handleRenameClick = () => {
+ this.setState({ openModal: ProfileActionModals.Rename });
};
handleDeleteClick = () => {
- this.setState({ deleteFormOpen: true });
+ this.setState({ openModal: ProfileActionModals.Delete });
};
- handleExtendClick = () => {
- this.setState({ extendFormOpen: true });
- };
+ handleProfileCopy = async (name: string) => {
+ this.setState({ loading: true });
- handleRenameClick = () => {
- this.setState({ renameFormOpen: true });
+ try {
+ await copyProfile(this.props.profile.key, name);
+ this.profileActionPerformed(name);
+ } catch {
+ this.profileActionError();
+ }
};
- handleProfileCopy = (name: string) => {
- this.closeCopyForm();
- this.navigateToNewProfile(name);
- };
+ handleProfileExtend = async (name: string) => {
+ const { profile: parentProfile } = this.props;
+
+ const data = {
+ language: parentProfile.language,
+ name
+ };
+
+ this.setState({ loading: true });
- handleProfileDelete = () => {
- this.props.router.replace(PROFILE_PATH);
- this.props.updateProfiles();
+ try {
+ const { profile: newProfile } = await createQualityProfile(data);
+ await changeProfileParent(newProfile, parentProfile);
+ this.profileActionPerformed(name);
+ } catch {
+ this.profileActionError();
+ }
};
- handleProfileExtend = (name: string) => {
- this.closeExtendForm();
- this.navigateToNewProfile(name);
+ handleProfileRename = async (name: string) => {
+ this.setState({ loading: true });
+
+ try {
+ await renameProfile(this.props.profile.key, name);
+ this.profileActionPerformed(name);
+ } catch {
+ this.profileActionError();
+ }
};
- handleProfileRename = (name: string) => {
- this.closeRenameForm();
- this.props.updateProfiles().then(
- () => {
- if (!this.props.fromList) {
- this.props.router.replace(getProfilePath(name, this.props.profile.language));
- }
- },
- () => {}
- );
+ handleProfileDelete = async () => {
+ this.setState({ loading: true });
+
+ try {
+ await deleteProfile(this.props.profile);
+
+ if (this.mounted) {
+ this.setState({ loading: false, openModal: undefined });
+ this.props.router.replace(PROFILE_PATH);
+ this.props.updateProfiles();
+ }
+ } catch {
+ this.profileActionError();
+ }
};
handleSetDefaultClick = () => {
setDefaultProfile(this.props.profile).then(this.props.updateProfiles, () => {});
};
- navigateToNewProfile = (name: string) => {
- this.props.updateProfiles().then(
- () => {
- this.props.router.push(getProfilePath(name, this.props.profile.language));
- },
- () => {}
- );
+ profileActionPerformed = (name: string) => {
+ const { profile, router } = this.props;
+ if (this.mounted) {
+ this.setState({ loading: false, openModal: undefined });
+ this.props.updateProfiles().then(
+ () => {
+ router.push(getProfilePath(name, profile.language));
+ },
+ () => {
+ /* noop */
+ }
+ );
+ }
+ };
+
+ profileActionError = () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
};
render() {
const { profile } = this.props;
+ const { loading, openModal } = this.state;
const { actions = {} } = profile;
const backupUrl = `${getBaseUrl()}${getQualityProfileBackupUrl(profile)}`;
<>
<ActionsDropdown className={this.props.className}>
{actions.edit && (
- <ActionsDropdownItem to={activateMoreUrl}>
- <span data-test="quality-profiles__activate-more-rules">
- {translate('quality_profiles.activate_more_rules')}
- </span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__activate-more-rules"
+ to={activateMoreUrl}>
+ {translate('quality_profiles.activate_more_rules')}
</ActionsDropdownItem>
)}
{!profile.isBuiltIn && (
- <ActionsDropdownItem download={`${profile.key}.xml`} to={backupUrl}>
- <span data-test="quality-profiles__backup">{translate('backup_verb')}</span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__backup"
+ download={`${profile.key}.xml`}
+ to={backupUrl}>
+ {translate('backup_verb')}
</ActionsDropdownItem>
)}
- <ActionsDropdownItem to={getProfileComparePath(profile.name, profile.language)}>
- <span data-test="quality-profiles__compare">{translate('compare')}</span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__compare"
+ to={getProfileComparePath(profile.name, profile.language)}>
+ {translate('compare')}
</ActionsDropdownItem>
{actions.copy && (
<>
- <ActionsDropdownItem onClick={this.handleCopyClick}>
- <span data-test="quality-profiles__copy">{translate('copy')}</span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__copy"
+ onClick={this.handleCopyClick}>
+ {translate('copy')}
</ActionsDropdownItem>
- <ActionsDropdownItem onClick={this.handleExtendClick}>
- <span data-test="quality-profiles__extend">{translate('extend')}</span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__extend"
+ onClick={this.handleExtendClick}>
+ {translate('extend')}
</ActionsDropdownItem>
</>
)}
{actions.edit && (
- <ActionsDropdownItem onClick={this.handleRenameClick}>
- <span data-test="quality-profiles__rename">{translate('rename')}</span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__rename"
+ onClick={this.handleRenameClick}>
+ {translate('rename')}
</ActionsDropdownItem>
)}
{actions.setAsDefault && (
- <ActionsDropdownItem onClick={this.handleSetDefaultClick}>
- <span data-test="quality-profiles__set-as-default">
- {translate('set_as_default')}
- </span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__set-as-default"
+ onClick={this.handleSetDefaultClick}>
+ {translate('set_as_default')}
</ActionsDropdownItem>
)}
{actions.delete && <ActionsDropdownDivider />}
{actions.delete && (
- <ActionsDropdownItem destructive={true} onClick={this.handleDeleteClick}>
- <span data-test="quality-profiles__delete">{translate('delete')}</span>
+ <ActionsDropdownItem
+ className="it__quality-profiles__delete"
+ destructive={true}
+ onClick={this.handleDeleteClick}>
+ {translate('delete')}
</ActionsDropdownItem>
)}
</ActionsDropdown>
- {this.state.copyFormOpen && (
- <CopyProfileForm
- onClose={this.closeCopyForm}
- onCopy={this.handleProfileCopy}
+ {openModal === ProfileActionModals.Copy && (
+ <ProfileModalForm
+ btnLabelKey="copy"
+ headerKey="quality_profiles.copy_x_title"
+ loading={loading}
+ onClose={this.handleCloseModal}
+ onSubmit={this.handleProfileCopy}
profile={profile}
/>
)}
- {this.state.extendFormOpen && (
- <ExtendProfileForm
- onClose={this.closeExtendForm}
- onExtend={this.handleProfileExtend}
+ {openModal === ProfileActionModals.Extend && (
+ <ProfileModalForm
+ btnLabelKey="extend"
+ headerKey="quality_profiles.extend_x_title"
+ loading={loading}
+ onClose={this.handleCloseModal}
+ onSubmit={this.handleProfileExtend}
profile={profile}
/>
)}
- {this.state.deleteFormOpen && (
- <DeleteProfileForm
- onClose={this.closeDeleteForm}
- onDelete={this.handleProfileDelete}
+ {openModal === ProfileActionModals.Rename && (
+ <ProfileModalForm
+ btnLabelKey="rename"
+ headerKey="quality_profiles.rename_x_title"
+ loading={loading}
+ onClose={this.handleCloseModal}
+ onSubmit={this.handleProfileRename}
profile={profile}
/>
)}
- {this.state.renameFormOpen && (
- <RenameProfileForm
- onClose={this.closeRenameForm}
- onRename={this.handleProfileRename}
+ {openModal === ProfileActionModals.Delete && (
+ <DeleteProfileForm
+ loading={loading}
+ onClose={this.handleCloseModal}
+ onDelete={this.handleProfileDelete}
profile={profile}
/>
)}
--- /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 { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
+import Modal from 'sonar-ui-common/components/controls/Modal';
+import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker';
+import MandatoryFieldsExplanation from 'sonar-ui-common/components/ui/MandatoryFieldsExplanation';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import { Profile } from '../types';
+
+export interface ProfileModalFormProps {
+ btnLabelKey: string;
+ headerKey: string;
+ loading: boolean;
+ onClose: () => void;
+ onSubmit: (name: string) => void;
+ profile: Profile;
+}
+
+export default function ProfileModalForm(props: ProfileModalFormProps) {
+ const { btnLabelKey, headerKey, loading, profile } = props;
+ const [name, setName] = React.useState<string | undefined>(undefined);
+
+ const submitDisabled = loading || !name || name === profile.name;
+ const header = translateWithParameters(headerKey, profile.name, profile.languageName);
+
+ return (
+ <Modal contentLabel={header} onRequestClose={props.onClose} size="small">
+ <form
+ onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
+ e.preventDefault();
+ if (name) {
+ props.onSubmit(name);
+ }
+ }}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <MandatoryFieldsExplanation className="modal-field" />
+ <div className="modal-field">
+ <label htmlFor="profile-name">
+ {translate('quality_profiles.new_name')}
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ autoFocus={true}
+ id="profile-name"
+ maxLength={100}
+ name="name"
+ onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
+ setName(e.currentTarget.value);
+ }}
+ required={true}
+ size={50}
+ type="text"
+ value={name ?? profile.name}
+ />
+ </div>
+ </div>
+ <div className="modal-foot">
+ {loading && <i className="spinner spacer-right" />}
+ <SubmitButton disabled={submitDisabled}>{translate(btnLabelKey)}</SubmitButton>
+ <ResetButtonLink onClick={props.onClose}>{translate('cancel')}</ResetButtonLink>
+ </div>
+ </form>
+ </Modal>
+ );
+}
+++ /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 { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
-import Modal from 'sonar-ui-common/components/controls/Modal';
-import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker';
-import MandatoryFieldsExplanation from 'sonar-ui-common/components/ui/MandatoryFieldsExplanation';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { renameProfile } from '../../../api/quality-profiles';
-import { Profile } from '../types';
-
-interface Props {
- onClose: () => void;
- onRename: (name: string) => void;
- profile: Profile;
-}
-
-interface State {
- loading: boolean;
- name: string | null;
-}
-
-export default class RenameProfileForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: false, name: null };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
- this.setState({ name: event.currentTarget.value });
- };
-
- handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
- event.preventDefault();
-
- const { name } = this.state;
-
- if (name != null) {
- this.setState({ loading: true });
- renameProfile(this.props.profile.key, name).then(
- () => this.props.onRename(name),
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- );
- }
- };
-
- render() {
- const { profile } = this.props;
- const header = translateWithParameters(
- 'quality_profiles.rename_x_title',
- profile.name,
- profile.languageName
- );
- const submitDisabled =
- this.state.loading || !this.state.name || this.state.name === profile.name;
-
- return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose} size="small">
- <form id="rename-profile-form" onSubmit={this.handleFormSubmit}>
- <div className="modal-head">
- <h2>{header}</h2>
- </div>
- <div className="modal-body">
- <MandatoryFieldsExplanation className="modal-field" />
- <div className="modal-field">
- <label htmlFor="rename-profile-name">
- {translate('quality_profiles.new_name')}
- <MandatoryFieldMarker />
- </label>
- <input
- autoFocus={true}
- id="rename-profile-name"
- maxLength={100}
- name="name"
- onChange={this.handleNameChange}
- required={true}
- size={50}
- type="text"
- value={this.state.name != null ? this.state.name : profile.name}
- />
- </div>
- </div>
- <div className="modal-foot">
- {this.state.loading && <i className="spinner spacer-right" />}
- <SubmitButton disabled={submitDisabled} id="rename-profile-submit">
- {translate('rename')}
- </SubmitButton>
- <ResetButtonLink id="rename-profile-cancel" onClick={this.props.onClose}>
- {translate('cancel')}
- </ResetButtonLink>
- </div>
- </form>
- </Modal>
- );
- }
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { shallow } from 'enzyme';
import * as React from 'react';
-import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { deleteProfile } from '../../../../api/quality-profiles';
import { mockEvent, mockQualityProfile } from '../../../../helpers/testMocks';
-import DeleteProfileForm from '../DeleteProfileForm';
-
-beforeEach(() => jest.clearAllMocks());
-
-jest.mock('../../../../api/quality-profiles', () => ({
- deleteProfile: jest.fn().mockResolvedValue({})
-}));
+import DeleteProfileForm, { DeleteProfileFormProps } from '../DeleteProfileForm';
it('should render correctly', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
+ expect(shallowRender({ profile: mockQualityProfile({ childrenCount: 2 }) })).toMatchSnapshot(
+ 'profile has children'
+ );
});
-it('should handle form submit correctly', async () => {
- const wrapper = shallowRender();
- wrapper.instance().handleFormSubmit(mockEvent());
- await waitAndUpdate(wrapper);
+it('should correctly submit the form', () => {
+ const onDelete = jest.fn();
+ const wrapper = shallowRender({ onDelete });
- expect(deleteProfile).toHaveBeenCalled();
+ const formOnSubmit = wrapper.find('form').props().onSubmit;
+ if (formOnSubmit) {
+ formOnSubmit(mockEvent());
+ }
+ expect(onDelete).toBeCalled();
});
-function shallowRender(props: Partial<DeleteProfileForm['props']> = {}) {
- return shallow<DeleteProfileForm>(
+function shallowRender(props: Partial<DeleteProfileFormProps> = {}) {
+ return shallow<DeleteProfileFormProps>(
<DeleteProfileForm
+ loading={false}
onClose={jest.fn()}
onDelete={jest.fn()}
profile={mockQualityProfile()}
+++ /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 { mockEvent, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { changeProfileParent, createQualityProfile } from '../../../../api/quality-profiles';
-import { mockQualityProfile } from '../../../../helpers/testMocks';
-import ExtendProfileForm from '../ExtendProfileForm';
-
-jest.mock('../../../../api/quality-profiles', () => ({
- createQualityProfile: jest.fn().mockResolvedValue({ profile: { key: 'new-profile' } }),
- changeProfileParent: jest.fn().mockResolvedValue(true)
-}));
-
-it('should render correctly', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should correctly create a new profile and extend the existing one', async () => {
- const profile = mockQualityProfile();
- const name = 'New name';
- const wrapper = shallowRender({ profile });
-
- expect(wrapper.find('SubmitButton').props().disabled).toBe(true);
-
- wrapper.setState({ name }).update();
- wrapper.instance().handleFormSubmit(mockEvent());
- await waitAndUpdate(wrapper);
-
- const data = new FormData();
- data.append('language', profile.language);
- data.append('name', name);
- expect(createQualityProfile).toHaveBeenCalledWith(data);
- expect(changeProfileParent).toHaveBeenCalledWith({ key: 'new-profile' }, profile);
-});
-
-function shallowRender(props: Partial<ExtendProfileForm['props']> = {}) {
- return shallow<ExtendProfileForm>(
- <ExtendProfileForm
- onClose={jest.fn()}
- onExtend={jest.fn()}
- profile={mockQualityProfile()}
- {...props}
- />
- );
-}
import { shallow } from 'enzyme';
import * as React from 'react';
import { click, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { setDefaultProfile } from '../../../../api/quality-profiles';
+import {
+ changeProfileParent,
+ copyProfile,
+ createQualityProfile,
+ deleteProfile,
+ renameProfile,
+ setDefaultProfile
+} from '../../../../api/quality-profiles';
import { mockQualityProfile, mockRouter } from '../../../../helpers/testMocks';
+import { ProfileActionModals } from '../../types';
+import { PROFILE_PATH } from '../../utils';
+import DeleteProfileForm from '../DeleteProfileForm';
import { ProfileActions } from '../ProfileActions';
+import ProfileModalForm from '../ProfileModalForm';
-beforeEach(() => jest.clearAllMocks());
+jest.mock('../../../../api/quality-profiles', () => {
+ const { mockQualityProfile } = jest.requireActual('../../../../helpers/testMocks');
-jest.mock('../../../../api/quality-profiles', () => ({
- ...jest.requireActual('../../../../api/quality-profiles'),
- setDefaultProfile: jest.fn().mockResolvedValue({})
-}));
+ return {
+ ...jest.requireActual('../../../../api/quality-profiles'),
+ copyProfile: jest.fn().mockResolvedValue(null),
+ changeProfileParent: jest.fn().mockResolvedValue(null),
+ createQualityProfile: jest
+ .fn()
+ .mockResolvedValue({ profile: mockQualityProfile({ key: 'newProfile' }) }),
+ deleteProfile: jest.fn().mockResolvedValue(null),
+ setDefaultProfile: jest.fn().mockResolvedValue(null),
+ renameProfile: jest.fn().mockResolvedValue(null)
+ };
+});
const PROFILE = mockQualityProfile({
activeRuleCount: 68,
rulesUpdatedAt: '2017-06-28T12:58:44+0000'
});
-it('renders with no permissions', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('renders with permission to edit only', () => {
- expect(shallowRender({ profile: { ...PROFILE, actions: { edit: true } } })).toMatchSnapshot();
-});
+beforeEach(() => jest.clearAllMocks());
-it('renders with all permissions', () => {
+it('renders correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('no permissions');
+ expect(shallowRender({ profile: { ...PROFILE, actions: { edit: true } } })).toMatchSnapshot(
+ 'edit only'
+ );
expect(
shallowRender({
profile: {
}
}
})
- ).toMatchSnapshot();
+ ).toMatchSnapshot('all permissions');
+
+ expect(shallowRender().setState({ openModal: ProfileActionModals.Copy })).toMatchSnapshot(
+ 'copy modal'
+ );
+ expect(shallowRender().setState({ openModal: ProfileActionModals.Extend })).toMatchSnapshot(
+ 'extend modal'
+ );
+ expect(shallowRender().setState({ openModal: ProfileActionModals.Rename })).toMatchSnapshot(
+ 'rename modal'
+ );
+ expect(shallowRender().setState({ openModal: ProfileActionModals.Delete })).toMatchSnapshot(
+ 'delete modal'
+ );
});
-it('should copy profile', async () => {
- const name = 'new-name';
- const updateProfiles = jest.fn(() => Promise.resolve());
- const push = jest.fn();
- const wrapper = shallowRender({
- profile: { ...PROFILE, actions: { copy: true } },
- router: { push, replace: jest.fn() },
- updateProfiles
+describe('copy a profile', () => {
+ it('should correctly copy a profile', async () => {
+ const name = 'new-name';
+ const updateProfiles = jest.fn().mockResolvedValue(null);
+ const push = jest.fn();
+ const wrapper = shallowRender({
+ profile: { ...PROFILE, actions: { copy: true } },
+ router: mockRouter({ push }),
+ updateProfiles
+ });
+
+ click(wrapper.find('.it__quality-profiles__copy'));
+ expect(wrapper.find(ProfileModalForm).exists()).toBe(true);
+
+ wrapper
+ .find(ProfileModalForm)
+ .props()
+ .onSubmit(name);
+ expect(copyProfile).toBeCalledWith(PROFILE.key, name);
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).toBeCalled();
+ expect(push).toBeCalledWith({
+ pathname: '/profiles/show',
+ query: { language: 'js', name }
+ });
+ expect(wrapper.find(ProfileModalForm).exists()).toBe(false);
});
- click(wrapper.find('[data-test="quality-profiles__copy"]').parent());
- expect(wrapper.find('CopyProfileForm').exists()).toBe(true);
+ it('should correctly keep the modal open in case of an error', async () => {
+ (copyProfile as jest.Mock).mockRejectedValueOnce(null);
- wrapper.find('CopyProfileForm').prop<Function>('onCopy')(name);
- expect(updateProfiles).toBeCalled();
- await waitAndUpdate(wrapper);
+ const name = 'new-name';
+ const updateProfiles = jest.fn();
+ const push = jest.fn();
+ const wrapper = shallowRender({
+ profile: { ...PROFILE, actions: { copy: true } },
+ router: mockRouter({ push }),
+ updateProfiles
+ });
+ wrapper.setState({ openModal: ProfileActionModals.Copy });
+
+ wrapper.instance().handleProfileCopy(name);
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).not.toBeCalled();
+ await waitAndUpdate(wrapper);
- expect(push).toBeCalledWith({
- pathname: '/profiles/show',
- query: { language: 'js', name }
+ expect(push).not.toBeCalled();
+ expect(wrapper.state().openModal).toBe(ProfileActionModals.Copy);
});
- expect(wrapper.find('CopyProfileForm').exists()).toBe(false);
});
-it('should extend profile', async () => {
- const name = 'new-name';
- const updateProfiles = jest.fn(() => Promise.resolve());
- const push = jest.fn();
- const wrapper = shallowRender({
- profile: { ...PROFILE, actions: { copy: true } },
- router: { push, replace: jest.fn() },
- updateProfiles
+describe('extend a profile', () => {
+ it('should correctly extend a profile', async () => {
+ const name = 'new-name';
+ const profile = { ...PROFILE, actions: { copy: true } };
+ const updateProfiles = jest.fn().mockResolvedValue(null);
+ const push = jest.fn();
+ const wrapper = shallowRender({
+ profile,
+ router: mockRouter({ push }),
+ updateProfiles
+ });
+
+ click(wrapper.find('.it__quality-profiles__extend'));
+ expect(wrapper.find(ProfileModalForm).exists()).toBe(true);
+
+ wrapper
+ .find(ProfileModalForm)
+ .props()
+ .onSubmit(name);
+ expect(createQualityProfile).toBeCalledWith({ language: profile.language, name });
+ await waitAndUpdate(wrapper);
+ expect(changeProfileParent).toBeCalledWith(
+ expect.objectContaining({
+ key: 'newProfile'
+ }),
+ profile
+ );
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).toBeCalled();
+ await waitAndUpdate(wrapper);
+
+ expect(push).toBeCalledWith({
+ pathname: '/profiles/show',
+ query: { language: 'js', name }
+ });
+ expect(wrapper.find(ProfileModalForm).exists()).toBe(false);
});
- click(wrapper.find('[data-test="quality-profiles__extend"]').parent());
- expect(wrapper.find('ExtendProfileForm').exists()).toBe(true);
+ it('should correctly keep the modal open in case of an error', async () => {
+ (createQualityProfile as jest.Mock).mockRejectedValueOnce(null);
- wrapper.find('ExtendProfileForm').prop<Function>('onExtend')(name);
- expect(updateProfiles).toBeCalled();
- await waitAndUpdate(wrapper);
+ const name = 'new-name';
+ const updateProfiles = jest.fn();
+ const push = jest.fn();
+ const wrapper = shallowRender({
+ profile: { ...PROFILE, actions: { copy: true } },
+ router: mockRouter({ push }),
+ updateProfiles
+ });
+ wrapper.setState({ openModal: ProfileActionModals.Extend });
+
+ wrapper.instance().handleProfileExtend(name);
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).not.toBeCalled();
+ expect(changeProfileParent).not.toBeCalled();
+ expect(push).not.toBeCalled();
+ expect(wrapper.state().openModal).toBe(ProfileActionModals.Extend);
+ });
+});
+
+describe('rename a profile', () => {
+ it('should correctly rename a profile', async () => {
+ const name = 'new-name';
+ const updateProfiles = jest.fn().mockResolvedValue(null);
+ const push = jest.fn();
+ const wrapper = shallowRender({
+ profile: { ...PROFILE, actions: { edit: true } },
+ router: mockRouter({ push }),
+ updateProfiles
+ });
+
+ click(wrapper.find('.it__quality-profiles__rename'));
+ expect(wrapper.find(ProfileModalForm).exists()).toBe(true);
+
+ wrapper
+ .find(ProfileModalForm)
+ .props()
+ .onSubmit(name);
+ expect(renameProfile).toBeCalledWith(PROFILE.key, name);
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).toBeCalled();
+ expect(push).toBeCalledWith({
+ pathname: '/profiles/show',
+ query: { language: 'js', name }
+ });
+ expect(wrapper.find(ProfileModalForm).exists()).toBe(false);
+ });
+
+ it('should correctly keep the modal open in case of an error', async () => {
+ (renameProfile as jest.Mock).mockRejectedValueOnce(null);
+
+ const name = 'new-name';
+ const updateProfiles = jest.fn();
+ const push = jest.fn();
+ const wrapper = shallowRender({
+ profile: { ...PROFILE, actions: { copy: true } },
+ router: mockRouter({ push }),
+ updateProfiles
+ });
+ wrapper.setState({ openModal: ProfileActionModals.Rename });
+
+ wrapper.instance().handleProfileRename(name);
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).not.toBeCalled();
+ await waitAndUpdate(wrapper);
+
+ expect(push).not.toBeCalled();
+ expect(wrapper.state().openModal).toBe(ProfileActionModals.Rename);
+ });
+});
+
+describe('delete a profile', () => {
+ it('should correctly delete a profile', async () => {
+ const updateProfiles = jest.fn().mockResolvedValue(null);
+ const replace = jest.fn();
+ const profile = { ...PROFILE, actions: { delete: true } };
+ const wrapper = shallowRender({
+ profile,
+ router: mockRouter({ replace }),
+ updateProfiles
+ });
+
+ click(wrapper.find('.it__quality-profiles__delete'));
+ expect(wrapper.find(DeleteProfileForm).exists()).toBe(true);
+
+ wrapper
+ .find(DeleteProfileForm)
+ .props()
+ .onDelete();
+ expect(deleteProfile).toBeCalledWith(profile);
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).toBeCalled();
+ expect(replace).toBeCalledWith(PROFILE_PATH);
+ expect(wrapper.find(ProfileModalForm).exists()).toBe(false);
+ });
+
+ it('should correctly keep the modal open in case of an error', async () => {
+ (deleteProfile as jest.Mock).mockRejectedValueOnce(null);
+
+ const updateProfiles = jest.fn();
+ const replace = jest.fn();
+ const wrapper = shallowRender({
+ profile: { ...PROFILE, actions: { copy: true } },
+ router: mockRouter({ replace }),
+ updateProfiles
+ });
+ wrapper.setState({ openModal: ProfileActionModals.Delete });
+
+ wrapper.instance().handleProfileDelete();
+ await waitAndUpdate(wrapper);
+
+ expect(updateProfiles).not.toBeCalled();
+ await waitAndUpdate(wrapper);
- expect(push).toBeCalledWith({
- pathname: '/profiles/show',
- query: { language: 'js', name }
+ expect(replace).not.toBeCalled();
+ expect(wrapper.state().openModal).toBe(ProfileActionModals.Delete);
});
- expect(wrapper.find('ExtendProfileForm').exists()).toBe(false);
});
-it('should delete profile properly', async () => {
+it('should correctly set a profile as the default', async () => {
const updateProfiles = jest.fn();
const wrapper = shallowRender({ updateProfiles });
--- /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 { mockEvent, mockQualityProfile } from '../../../../helpers/testMocks';
+import ProfileModalForm, { ProfileModalFormProps } from '../ProfileModalForm';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
+
+ const wrapper = shallowRender();
+ change(wrapper.find('#profile-name'), 'new name');
+ expect(wrapper).toMatchSnapshot('can submit');
+});
+
+it('should correctly submit the form', () => {
+ const onSubmit = jest.fn();
+ const wrapper = shallowRender({ onSubmit });
+
+ // Won't submit unless a new name was given.
+ let formOnSubmit = wrapper.find('form').props().onSubmit;
+ if (formOnSubmit) {
+ formOnSubmit(mockEvent());
+ }
+ expect(onSubmit).not.toBeCalled();
+
+ // Input a new name.
+ change(wrapper.find('#profile-name'), 'new name');
+
+ // Now will submit the form.
+ formOnSubmit = wrapper.find('form').props().onSubmit;
+ if (formOnSubmit) {
+ formOnSubmit(mockEvent());
+ }
+ expect(onSubmit).toBeCalledWith('new name');
+});
+
+function shallowRender(props: Partial<ProfileModalFormProps> = {}) {
+ return shallow<ProfileModalFormProps>(
+ <ProfileModalForm
+ btnLabelKey="btn-label"
+ headerKey="header-label"
+ loading={false}
+ onClose={jest.fn()}
+ onSubmit={jest.fn()}
+ profile={mockQualityProfile()}
+ {...props}
+ />
+ );
+}
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should render correctly 1`] = `
+exports[`should render correctly: default 1`] = `
+<Modal
+ contentLabel="quality_profiles.delete_confirm_title"
+ onRequestClose={[MockFunction]}
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ quality_profiles.delete_confirm_title
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <p>
+ quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript
+ </p>
+ </div>
+ <div
+ className="modal-foot"
+ >
+ <SubmitButton
+ className="button-red"
+ disabled={false}
+ >
+ delete
+ </SubmitButton>
+ <ResetButtonLink
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
+
+exports[`should render correctly: loading 1`] = `
<Modal
contentLabel="quality_profiles.delete_confirm_title"
onRequestClose={[MockFunction]}
>
<form
- id="delete-profile-form"
onSubmit={[Function]}
>
<div
<div
className="modal-body"
>
- <div
- className="js-modal-messages"
- />
<p>
quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript
</p>
</div>
+ <div
+ className="modal-foot"
+ >
+ <i
+ className="spinner spacer-right"
+ />
+ <SubmitButton
+ className="button-red"
+ disabled={true}
+ >
+ delete
+ </SubmitButton>
+ <ResetButtonLink
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
+
+exports[`should render correctly: profile has children 1`] = `
+<Modal
+ contentLabel="quality_profiles.delete_confirm_title"
+ onRequestClose={[MockFunction]}
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ quality_profiles.delete_confirm_title
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <div>
+ <Alert
+ variant="warning"
+ >
+ quality_profiles.this_profile_has_descendants
+ </Alert>
+ <p>
+ quality_profiles.are_you_sure_want_delete_profile_x_and_descendants.name.JavaScript
+ </p>
+ </div>
+ </div>
<div
className="modal-foot"
>
<SubmitButton
className="button-red"
disabled={false}
- id="delete-profile-submit"
>
delete
</SubmitButton>
<ResetButtonLink
- id="delete-profile-cancel"
onClick={[MockFunction]}
>
cancel
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Modal
- contentLabel="quality_profiles.extend_x_title.name.JavaScript"
- onRequestClose={[MockFunction]}
- size="small"
->
- <form
- onSubmit={[Function]}
- >
- <div
- className="modal-head"
- >
- <h2>
- quality_profiles.extend_x_title.name.JavaScript
- </h2>
- </div>
- <div
- className="modal-body"
- >
- <MandatoryFieldsExplanation
- className="modal-field"
- />
- <div
- className="modal-field"
- >
- <label
- htmlFor="extend-profile-name"
- >
- quality_profiles.copy_new_name
- <MandatoryFieldMarker />
- </label>
- <input
- autoFocus={true}
- id="extend-profile-name"
- maxLength={100}
- name="name"
- onChange={[Function]}
- required={true}
- size={50}
- type="text"
- value=""
- />
- </div>
- </div>
- <div
- className="modal-foot"
- >
- <DeferredSpinner
- className="spacer-right"
- loading={false}
- />
- <SubmitButton
- disabled={true}
- id="extend-profile-submit"
- >
- extend
- </SubmitButton>
- <ResetButtonLink
- id="extend-profile-cancel"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </div>
- </form>
-</Modal>
-`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`renders with all permissions 1`] = `
+exports[`renders correctly: all permissions 1`] = `
<Fragment>
<ActionsDropdown>
<ActionsDropdownItem
+ className="it__quality-profiles__activate-more-rules"
to={
Object {
"pathname": "/coding_rules",
}
}
>
- <span
- data-test="quality-profiles__activate-more-rules"
- >
- quality_profiles.activate_more_rules
- </span>
+ quality_profiles.activate_more_rules
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__backup"
download="key.xml"
to="/api/qualityprofiles/backup?language=js&qualityProfile=name"
>
- <span
- data-test="quality-profiles__backup"
- >
- backup_verb
- </span>
+ backup_verb
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__compare"
to={
Object {
"pathname": "/profiles/compare",
}
}
>
- <span
- data-test="quality-profiles__compare"
- >
- compare
- </span>
+ compare
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__copy"
onClick={[Function]}
>
- <span
- data-test="quality-profiles__copy"
- >
- copy
- </span>
+ copy
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__extend"
onClick={[Function]}
>
- <span
- data-test="quality-profiles__extend"
- >
- extend
- </span>
+ extend
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__rename"
onClick={[Function]}
>
- <span
- data-test="quality-profiles__rename"
- >
- rename
- </span>
+ rename
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__set-as-default"
onClick={[Function]}
>
- <span
- data-test="quality-profiles__set-as-default"
- >
- set_as_default
- </span>
+ set_as_default
</ActionsDropdownItem>
<ActionsDropdownDivider />
<ActionsDropdownItem
+ className="it__quality-profiles__delete"
destructive={true}
onClick={[Function]}
>
- <span
- data-test="quality-profiles__delete"
- >
- delete
- </span>
+ delete
</ActionsDropdownItem>
</ActionsDropdown>
</Fragment>
`;
-exports[`renders with no permissions 1`] = `
+exports[`renders correctly: copy modal 1`] = `
<Fragment>
<ActionsDropdown>
<ActionsDropdownItem
+ className="it__quality-profiles__backup"
download="key.xml"
to="/api/qualityprofiles/backup?language=js&qualityProfile=name"
>
- <span
- data-test="quality-profiles__backup"
- >
- backup_verb
- </span>
+ backup_verb
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__compare"
to={
Object {
"pathname": "/profiles/compare",
}
}
>
- <span
- data-test="quality-profiles__compare"
- >
- compare
- </span>
+ compare
</ActionsDropdownItem>
</ActionsDropdown>
+ <ProfileModalForm
+ btnLabelKey="copy"
+ headerKey="quality_profiles.copy_x_title"
+ loading={false}
+ onClose={[Function]}
+ onSubmit={[Function]}
+ profile={
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 68,
+ "childrenCount": 0,
+ "depth": 0,
+ "isBuiltIn": false,
+ "isDefault": false,
+ "isInherited": false,
+ "key": "key",
+ "language": "js",
+ "languageName": "JavaScript",
+ "name": "name",
+ "projectCount": 3,
+ "rulesUpdatedAt": "2017-06-28T12:58:44+0000",
+ }
+ }
+ />
+</Fragment>
+`;
+
+exports[`renders correctly: delete modal 1`] = `
+<Fragment>
+ <ActionsDropdown>
+ <ActionsDropdownItem
+ className="it__quality-profiles__backup"
+ download="key.xml"
+ to="/api/qualityprofiles/backup?language=js&qualityProfile=name"
+ >
+ backup_verb
+ </ActionsDropdownItem>
+ <ActionsDropdownItem
+ className="it__quality-profiles__compare"
+ to={
+ Object {
+ "pathname": "/profiles/compare",
+ "query": Object {
+ "language": "js",
+ "name": "name",
+ },
+ }
+ }
+ >
+ compare
+ </ActionsDropdownItem>
+ </ActionsDropdown>
+ <DeleteProfileForm
+ loading={false}
+ onClose={[Function]}
+ onDelete={[Function]}
+ profile={
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 68,
+ "childrenCount": 0,
+ "depth": 0,
+ "isBuiltIn": false,
+ "isDefault": false,
+ "isInherited": false,
+ "key": "key",
+ "language": "js",
+ "languageName": "JavaScript",
+ "name": "name",
+ "projectCount": 3,
+ "rulesUpdatedAt": "2017-06-28T12:58:44+0000",
+ }
+ }
+ />
</Fragment>
`;
-exports[`renders with permission to edit only 1`] = `
+exports[`renders correctly: edit only 1`] = `
<Fragment>
<ActionsDropdown>
<ActionsDropdownItem
+ className="it__quality-profiles__activate-more-rules"
to={
Object {
"pathname": "/coding_rules",
}
}
>
- <span
- data-test="quality-profiles__activate-more-rules"
- >
- quality_profiles.activate_more_rules
- </span>
+ quality_profiles.activate_more_rules
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__backup"
download="key.xml"
to="/api/qualityprofiles/backup?language=js&qualityProfile=name"
>
- <span
- data-test="quality-profiles__backup"
- >
- backup_verb
- </span>
+ backup_verb
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__compare"
to={
Object {
"pathname": "/profiles/compare",
}
}
>
- <span
- data-test="quality-profiles__compare"
- >
- compare
- </span>
+ compare
</ActionsDropdownItem>
<ActionsDropdownItem
+ className="it__quality-profiles__rename"
onClick={[Function]}
>
- <span
- data-test="quality-profiles__rename"
- >
- rename
- </span>
+ rename
+ </ActionsDropdownItem>
+ </ActionsDropdown>
+</Fragment>
+`;
+
+exports[`renders correctly: extend modal 1`] = `
+<Fragment>
+ <ActionsDropdown>
+ <ActionsDropdownItem
+ className="it__quality-profiles__backup"
+ download="key.xml"
+ to="/api/qualityprofiles/backup?language=js&qualityProfile=name"
+ >
+ backup_verb
+ </ActionsDropdownItem>
+ <ActionsDropdownItem
+ className="it__quality-profiles__compare"
+ to={
+ Object {
+ "pathname": "/profiles/compare",
+ "query": Object {
+ "language": "js",
+ "name": "name",
+ },
+ }
+ }
+ >
+ compare
</ActionsDropdownItem>
</ActionsDropdown>
+ <ProfileModalForm
+ btnLabelKey="extend"
+ headerKey="quality_profiles.extend_x_title"
+ loading={false}
+ onClose={[Function]}
+ onSubmit={[Function]}
+ profile={
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 68,
+ "childrenCount": 0,
+ "depth": 0,
+ "isBuiltIn": false,
+ "isDefault": false,
+ "isInherited": false,
+ "key": "key",
+ "language": "js",
+ "languageName": "JavaScript",
+ "name": "name",
+ "projectCount": 3,
+ "rulesUpdatedAt": "2017-06-28T12:58:44+0000",
+ }
+ }
+ />
+</Fragment>
+`;
+
+exports[`renders correctly: no permissions 1`] = `
+<Fragment>
+ <ActionsDropdown>
+ <ActionsDropdownItem
+ className="it__quality-profiles__backup"
+ download="key.xml"
+ to="/api/qualityprofiles/backup?language=js&qualityProfile=name"
+ >
+ backup_verb
+ </ActionsDropdownItem>
+ <ActionsDropdownItem
+ className="it__quality-profiles__compare"
+ to={
+ Object {
+ "pathname": "/profiles/compare",
+ "query": Object {
+ "language": "js",
+ "name": "name",
+ },
+ }
+ }
+ >
+ compare
+ </ActionsDropdownItem>
+ </ActionsDropdown>
+</Fragment>
+`;
+
+exports[`renders correctly: rename modal 1`] = `
+<Fragment>
+ <ActionsDropdown>
+ <ActionsDropdownItem
+ className="it__quality-profiles__backup"
+ download="key.xml"
+ to="/api/qualityprofiles/backup?language=js&qualityProfile=name"
+ >
+ backup_verb
+ </ActionsDropdownItem>
+ <ActionsDropdownItem
+ className="it__quality-profiles__compare"
+ to={
+ Object {
+ "pathname": "/profiles/compare",
+ "query": Object {
+ "language": "js",
+ "name": "name",
+ },
+ }
+ }
+ >
+ compare
+ </ActionsDropdownItem>
+ </ActionsDropdown>
+ <ProfileModalForm
+ btnLabelKey="rename"
+ headerKey="quality_profiles.rename_x_title"
+ loading={false}
+ onClose={[Function]}
+ onSubmit={[Function]}
+ profile={
+ Object {
+ "activeDeprecatedRuleCount": 0,
+ "activeRuleCount": 68,
+ "childrenCount": 0,
+ "depth": 0,
+ "isBuiltIn": false,
+ "isDefault": false,
+ "isInherited": false,
+ "key": "key",
+ "language": "js",
+ "languageName": "JavaScript",
+ "name": "name",
+ "projectCount": 3,
+ "rulesUpdatedAt": "2017-06-28T12:58:44+0000",
+ }
+ }
+ />
</Fragment>
`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: can submit 1`] = `
+<Modal
+ contentLabel="header-label.name.JavaScript"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ header-label.name.JavaScript
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <MandatoryFieldsExplanation
+ className="modal-field"
+ />
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="profile-name"
+ >
+ quality_profiles.new_name
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ autoFocus={true}
+ id="profile-name"
+ maxLength={100}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ size={50}
+ type="text"
+ value="new name"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-foot"
+ >
+ <SubmitButton
+ disabled={false}
+ >
+ btn-label
+ </SubmitButton>
+ <ResetButtonLink
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
+
+exports[`should render correctly: default 1`] = `
+<Modal
+ contentLabel="header-label.name.JavaScript"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ header-label.name.JavaScript
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <MandatoryFieldsExplanation
+ className="modal-field"
+ />
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="profile-name"
+ >
+ quality_profiles.new_name
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ autoFocus={true}
+ id="profile-name"
+ maxLength={100}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ size={50}
+ type="text"
+ value="name"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-foot"
+ >
+ <SubmitButton
+ disabled={true}
+ >
+ btn-label
+ </SubmitButton>
+ <ResetButtonLink
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
+
+exports[`should render correctly: loading 1`] = `
+<Modal
+ contentLabel="header-label.name.JavaScript"
+ onRequestClose={[MockFunction]}
+ size="small"
+>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-head"
+ >
+ <h2>
+ header-label.name.JavaScript
+ </h2>
+ </div>
+ <div
+ className="modal-body"
+ >
+ <MandatoryFieldsExplanation
+ className="modal-field"
+ />
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="profile-name"
+ >
+ quality_profiles.new_name
+ <MandatoryFieldMarker />
+ </label>
+ <input
+ autoFocus={true}
+ id="profile-name"
+ maxLength={100}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ size={50}
+ type="text"
+ value="name"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-foot"
+ >
+ <i
+ className="spinner spacer-right"
+ />
+ <SubmitButton
+ disabled={true}
+ >
+ btn-label
+ </SubmitButton>
+ <ResetButtonLink
+ onClick={[MockFunction]}
+ >
+ cancel
+ </ResetButtonLink>
+ </div>
+ </form>
+</Modal>
+`;
* 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 classNames from 'classnames';
import * as React from 'react';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
const offset = 25 * depth;
return (
- <tr className={className} data-test={`quality-profiles__inheritance-${type}`}>
+ <tr className={classNames(`it__quality-profiles__inheritance-${type}`, className)}>
<td>
<div style={{ paddingLeft: offset }}>
{displayLink ? (
exports[`should render correctly 1`] = `
<tr
- data-test="quality-profiles__inheritance-current"
+ className="it__quality-profiles__inheritance-current"
>
<td>
<div
exports[`should render correctly 2`] = `
<tr
- data-test="quality-profiles__inheritance-current"
+ className="it__quality-profiles__inheritance-current"
>
<td>
<div
exports[`should render correctly 3`] = `
<tr
- data-test="quality-profiles__inheritance-current"
+ className="it__quality-profiles__inheritance-current"
>
<td>
<div
exports[`should render correctly 4`] = `
<tr
- data-test="quality-profiles__inheritance-current"
+ className="it__quality-profiles__inheritance-current"
>
<td>
<div
ruleKey: string;
ruleName: string;
}
+
+export enum ProfileActionModals {
+ Copy,
+ Extend,
+ Rename,
+ Delete
+}
quality_profiles.x_rules_have_different_configuration={0} rules have a different configuration
quality_profiles.copy_x_title=Copy Profile "{0}" - {1}
quality_profiles.extend_x_title=Extend Profile "{0}" - {1}
-quality_profiles.copy_new_name=New name
quality_profiles.rename_x_title=Rename Profile {0} - {1}
quality_profiles.deprecated=deprecated
quality_profiles.severity_set_to=Severity set to