const { query } = this.props.location;
const { branches, component, loading } = this.state;
- if (loading) {
- return <i className="spinner" />;
- }
-
- if (!component) {
+ if (!loading && !component) {
return <ComponentContainerNotFound />;
}
const branch = branches.find(b => (query.branch ? b.name === query.branch : b.isMain));
- const isFile = ['FIL', 'UTS'].includes(component.qualifier);
- const configuration = component.configuration || {};
return (
<div>
- {!isFile && (
+ {component &&
+ !['FIL', 'UTS'].includes(component.qualifier) && (
<ComponentNav
branches={branches}
currentBranch={branch}
component={component}
- conf={configuration}
location={this.props.location}
+ onBranchesChange={this.handleBranchesChange}
/>
)}
- {React.cloneElement(this.props.children, {
- branch,
- branches,
- component: component,
- onBranchesChange: this.handleBranchesChange,
- onComponentChange: this.handleComponentChange
- })}
+ {loading ? (
+ <div className="page page-limited">
+ <i className="spinner" />
+ </div>
+ ) : (
+ React.cloneElement(this.props.children, {
+ branch,
+ branches,
+ component,
+ onComponentChange: this.handleComponentChange
+ })
+ )}
</div>
);
}
expect(wrapper.find(Inner).exists()).toBeTruthy();
});
});
-
-it('updates branches on change', () => {
- (getBranches as jest.Mock<any>).mockImplementation(() => Promise.resolve([]));
- const wrapper = shallow(
- <ComponentContainer location={{ query: { id: 'portfolioKey' } }}>
- <Inner />
- </ComponentContainer>
- );
- (wrapper.instance() as ComponentContainer).mounted = true;
- wrapper.setState({
- branches: [{ isMain: true }],
- component: { breadcrumbs: [{ key: 'projectKey', name: 'project', qualifier: 'TRK' }] },
- loading: false
- });
- (wrapper.find(Inner).prop('onBranchesChange') as Function)();
- expect(getBranches).toBeCalledWith('projectKey');
-});
.navbar-context-meta-branch-menu-item {
display: flex !important;
justify-content: space-between;
+ align-items: center;
+}
+
+.navbar-context-meta-branch-menu-item-name {
+ flex: 1;
+}
+
+.navbar-context-meta-branch-menu-item-actions {
+ height: 12px;
+ margin-left: 32px;
}
import ComponentNavMenu from './ComponentNavMenu';
import ComponentNavBranch from './ComponentNavBranch';
import RecentHistory from '../../RecentHistory';
-import { Branch, Component, ComponentConfiguration } from '../../../types';
+import { Branch, Component } from '../../../types';
import ContextNavBar from '../../../../components/nav/ContextNavBar';
import { getTasksForComponent } from '../../../../api/ce';
import { STATUSES } from '../../../../apps/background-tasks/constants';
branches: Branch[];
currentBranch?: Branch;
component: Component;
- conf: ComponentConfiguration;
location: {};
+ onBranchesChange: () => void;
}
interface State {
{this.props.currentBranch && (
<ComponentNavBranch
branches={this.props.branches}
+ component={this.props.component}
currentBranch={this.props.currentBranch}
// to close dropdown on any location change
location={this.props.location}
- project={this.props.component}
+ onBranchesChange={this.props.onBranchesChange}
/>
)}
<ComponentNavMeta
branch={this.props.currentBranch}
component={this.props.component}
- conf={this.props.conf}
incremental={this.state.incremental}
isInProgress={this.state.isInProgress}
isFailed={this.state.isFailed}
<ComponentNavMenu
branch={this.props.currentBranch}
component={this.props.component}
- conf={this.props.conf}
// to re-render selected menu item
location={this.props.location}
/>
import * as PropTypes from 'prop-types';
import ComponentNavBranchesMenu from './ComponentNavBranchesMenu';
import SingleBranchHelperPopup from './SingleBranchHelperPopup';
-import NoBranchSupportPopup from './NoBranchSupportPopup';
import { Branch, Component } from '../../../types';
import BranchIcon from '../../../../components/icons-components/BranchIcon';
import { isShortLivingBranch } from '../../../../helpers/branches';
interface Props {
branches: Branch[];
+ component: Component;
currentBranch: Branch;
location?: any;
- project: Component;
+ onBranchesChange: () => void;
}
interface State {
};
static contextTypes = {
- branchesEnabled: PropTypes.bool.isRequired,
- onSonarCloud: PropTypes.bool
+ branchesEnabled: PropTypes.bool.isRequired
};
componentDidMount() {
componentWillReceiveProps(nextProps: Props) {
if (
- nextProps.project !== this.props.project ||
- nextProps.currentBranch !== this.props.currentBranch ||
+ nextProps.component !== this.props.component ||
+ this.differentBranches(nextProps.currentBranch, this.props.currentBranch) ||
nextProps.location !== this.props.location
) {
this.setState({ dropdownOpen: false, singleBranchPopupOpen: false });
this.mounted = false;
}
+ differentBranches(a: Branch, b: Branch) {
+ // if main branch changes name, we should not close the dropdown
+ return a.isMain && b.isMain ? false : a.name !== b.name;
+ }
+
handleClick = (event: React.SyntheticEvent<HTMLElement>) => {
event.preventDefault();
event.stopPropagation();
event.currentTarget.blur();
- this.setState({ dropdownOpen: true });
+ this.setState(state => ({ dropdownOpen: !state.dropdownOpen }));
};
closeDropdown = () => {
};
renderDropdown = () => {
+ const { configuration } = this.props.component;
return this.state.dropdownOpen ? (
<ComponentNavBranchesMenu
branches={this.props.branches}
+ canAdmin={configuration && configuration.showSettings}
+ component={this.props.component}
currentBranch={this.props.currentBranch}
+ onBranchesChange={this.props.onBranchesChange}
onClose={this.closeDropdown}
- project={this.props.project}
/>
) : null;
};
</div>
);
- renderNoBranchSupportPopup = () => (
- <div className="display-inline-block spacer-left">
- <a className="link-no-underline" href="#" onClick={this.handleNoBranchSupportClick}>
- <HelpIcon fill="#cdcdcd" />
- </a>
- <BubblePopupHelper
- isOpen={this.state.noBranchSupportPopupOpen}
- position="bottomleft"
- popup={<NoBranchSupportPopup />}
- togglePopup={this.toggleNoBranchSupportPopup}
- />
- </div>
- );
-
render() {
const { branches, currentBranch } = this.props;
- if (this.context.onSonarCloud && !this.context.branchesEnabled) {
- return null;
- }
-
if (!this.context.branchesEnabled) {
- return (
- <div className="navbar-context-branches">
- <BranchIcon branch={currentBranch} className="little-spacer-right" color="#cdcdcd" />
- <span className="note">{currentBranch.name}</span>
- {this.renderNoBranchSupportPopup()}
- </div>
- );
+ return null;
}
if (branches.length < 2) {
} from '../../../../helpers/branches';
import { translate } from '../../../../helpers/l10n';
import { getProjectBranchUrl } from '../../../../helpers/urls';
-import { Link } from 'react-router';
import Tooltip from '../../../../components/controls/Tooltip';
interface Props {
branches: Branch[];
+ canAdmin?: boolean;
+ component: Component;
currentBranch: Branch;
+ onBranchesChange: () => void;
onClose: () => void;
- project: Component;
}
interface State {
);
handleClickOutside = (event: Event) => {
- if (!this.node || !this.node.contains(event.target as HTMLElement)) {
+ // do not close when rename or delete branch modal is open
+ const modal = document.querySelector('.modal');
+ if (!modal && (!this.node || !this.node.contains(event.target as HTMLElement))) {
this.props.onClose();
}
};
};
getSelected = () => {
+ if (this.state.selected) {
+ return this.state.selected;
+ }
+
const branches = this.getFilteredBranches();
- return this.state.selected || (branches.length > 0 && branches[0].name);
+ if (branches.find(b => b.name === this.props.currentBranch.name)) {
+ return this.props.currentBranch.name;
+ }
+
+ if (branches.length > 0) {
+ return branches[0].name;
+ }
+
+ return undefined;
};
- getProjectBranchUrl = (branch: Branch) => getProjectBranchUrl(this.props.project.key, branch);
+ getProjectBranchUrl = (branch: Branch) => getProjectBranchUrl(this.props.component.key, branch);
isSelected = (branch: Branch) => branch.name === this.getSelected();
menu.push(
<ComponentNavBranchesMenuItem
branch={branch}
- component={this.props.project}
+ canAdmin={this.props.canAdmin}
+ component={this.props.component}
key={branch.name}
+ onBranchesChange={this.props.onBranchesChange}
onSelect={this.handleSelect}
selected={branch.name === selected}
/>
};
render() {
- const { project } = this.props;
- const showManageLink =
- project.qualifier === 'TRK' && project.configuration && project.configuration.showSettings;
-
return (
<div className="dropdown-menu dropdown-menu-shadow" ref={node => (this.node = node)}>
{this.renderSearch()}
{this.renderBranchesList()}
- {showManageLink && (
- <div className="dropdown-bottom-hint text-right">
- <Link
- className="text-muted"
- to={{ pathname: '/project/branches', query: { id: project.key } }}>
- {translate('branches.manage')}
- </Link>
- </div>
- )}
</div>
);
}
import * as React from 'react';
import { Link } from 'react-router';
import * as classNames from 'classnames';
+import DeleteBranchModal from './DeleteBranchModal';
+import RenameBranchModal from './RenameBranchModal';
import BranchStatus from '../../../../components/common/BranchStatus';
import { Branch, Component } from '../../../types';
import BranchIcon from '../../../../components/icons-components/BranchIcon';
+import ChangeIcon from '../../../../components/icons-components/ChangeIcon';
+import DeleteIcon from '../../../../components/icons-components/DeleteIcon';
import { isShortLivingBranch } from '../../../../helpers/branches';
import { translate } from '../../../../helpers/l10n';
import { getProjectBranchUrl } from '../../../../helpers/urls';
-interface Props {
+export interface Props {
branch: Branch;
+ canAdmin?: boolean;
component: Component;
+ onBranchesChange: () => void;
onSelect: (branch: Branch) => void;
selected: boolean;
}
-export default function ComponentNavBranchesMenuItem({ branch, ...props }: Props) {
- const handleMouseEnter = () => {
- props.onSelect(branch);
+interface State {
+ deleteBranchModal: boolean;
+ renameBranchModal: boolean;
+}
+
+export default class ComponentNavBranchesMenuItem extends React.PureComponent<Props, State> {
+ state: State = { deleteBranchModal: false, renameBranchModal: false };
+
+ handleMouseEnter = () => {
+ this.props.onSelect(this.props.branch);
+ };
+
+ handleDeleteBranchClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ deleteBranchModal: true });
+ };
+
+ handleDeleteBranchClose = () => {
+ this.setState({ deleteBranchModal: false });
+ };
+
+ handleBranchDelete = () => {
+ this.props.onBranchesChange();
+ this.setState({ deleteBranchModal: false });
+ };
+
+ handleRenameBranchClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ renameBranchModal: true });
};
- return (
- <li key={branch.name} onMouseEnter={handleMouseEnter}>
- <Link
- className={classNames('navbar-context-meta-branch-menu-item', {
- active: props.selected
- })}
- to={getProjectBranchUrl(props.component.key, branch)}>
- <div>
- <BranchIcon
+ handleRenameBranchClose = () => {
+ this.setState({ renameBranchModal: false });
+ };
+
+ handleBranchRename = () => {
+ this.props.onBranchesChange();
+ this.setState({ renameBranchModal: false });
+ };
+
+ render() {
+ const { branch } = this.props;
+ return (
+ <li key={branch.name} onMouseEnter={this.handleMouseEnter}>
+ <Link
+ className={classNames('navbar-context-meta-branch-menu-item', {
+ active: this.props.selected
+ })}
+ to={getProjectBranchUrl(this.props.component.key, branch)}>
+ <div className="navbar-context-meta-branch-menu-item-name">
+ <BranchIcon
+ branch={branch}
+ className={classNames('little-spacer-right', {
+ 'big-spacer-left': isShortLivingBranch(branch) && !branch.isOrphan
+ })}
+ />
+ {branch.name}
+ {branch.isMain && (
+ <div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
+ )}
+ </div>
+ <div className="big-spacer-left note">
+ <BranchStatus branch={branch} concise={true} />
+ </div>
+ {this.props.canAdmin && (
+ <div className="navbar-context-meta-branch-menu-item-actions">
+ {branch.isMain ? (
+ <button className="js-rename button-link" onClick={this.handleRenameBranchClick}>
+ <ChangeIcon />
+ </button>
+ ) : (
+ <button className="js-delete button-link" onClick={this.handleDeleteBranchClick}>
+ <DeleteIcon />
+ </button>
+ )}
+ </div>
+ )}
+ </Link>
+
+ {this.state.deleteBranchModal && (
+ <DeleteBranchModal
branch={branch}
- className={classNames('little-spacer-right', {
- 'big-spacer-left': isShortLivingBranch(branch) && !branch.isOrphan
- })}
+ component={this.props.component.key}
+ onClose={this.handleDeleteBranchClose}
+ onDelete={this.handleBranchDelete}
/>
- {branch.name}
- {branch.isMain && (
- <div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
- )}
- </div>
- <div className="big-spacer-left note">
- <BranchStatus branch={branch} concise={true} />
- </div>
- </Link>
- </li>
- );
+ )}
+
+ {this.state.renameBranchModal && (
+ <RenameBranchModal
+ branch={branch}
+ component={this.props.component.key}
+ onClose={this.handleRenameBranchClose}
+ onRename={this.handleBranchRename}
+ />
+ )}
+ </li>
+ );
+ }
}
import { Link } from 'react-router';
import * as classNames from 'classnames';
import * as PropTypes from 'prop-types';
-import { Branch, Component, ComponentExtension, ComponentConfiguration } from '../../../types';
+import { Branch, Component, ComponentExtension } from '../../../types';
import NavBarTabs from '../../../../components/nav/NavBarTabs';
import {
isShortLivingBranch,
interface Props {
branch?: Branch;
component: Component;
- conf: ComponentConfiguration;
location?: any;
}
return this.props.component.qualifier === 'APP';
}
+ getConfiguration() {
+ return this.props.component.configuration || {};
+ }
+
renderDashboardLink() {
if (this.props.branch && isShortLivingBranch(this.props.branch)) {
return null;
renderAdministration() {
const { branch } = this.props;
- if (!this.props.conf.showSettings || (branch && isShortLivingBranch(branch))) {
+ if (!this.getConfiguration().showSettings || (branch && isShortLivingBranch(branch))) {
return null;
}
pathname: '/project/settings',
query: { branch: getBranchName(branch), id: this.props.component.key }
}}>
- {translate('layout.settings')}
+ {translate('branches.branch_settings')}
</Link>
</li>
);
renderAdministrationLinks() {
return [
this.renderSettingsLink(),
- this.renderBranchesLink(),
this.renderProfilesLink(),
this.renderQualityGateLink(),
this.renderCustomMeasuresLink(),
}
renderSettingsLink() {
- if (!this.props.conf.showSettings || this.isApplication() || this.isPortfolio()) {
+ if (!this.getConfiguration().showSettings || this.isApplication() || this.isPortfolio()) {
return null;
}
return (
);
}
- renderBranchesLink() {
- if (!this.context.branchesEnabled || !this.isProject() || !this.props.conf.showSettings) {
- return null;
- }
- return (
- <li key="branches">
- <Link
- to={{ pathname: '/project/branches', query: { id: this.props.component.key } }}
- activeClassName="active">
- {translate('project_branches.page')}
- </Link>
- </li>
- );
- }
-
renderProfilesLink() {
- if (!this.props.conf.showQualityProfiles) {
+ if (!this.getConfiguration().showQualityProfiles) {
return null;
}
return (
}
renderQualityGateLink() {
- if (!this.props.conf.showQualityGates) {
+ if (!this.getConfiguration().showQualityGates) {
return null;
}
return (
}
renderCustomMeasuresLink() {
- if (!this.props.conf.showManualMeasures) {
+ if (!this.getConfiguration().showManualMeasures) {
return null;
}
return (
}
renderLinksLink() {
- if (!this.props.conf.showLinks) {
+ if (!this.getConfiguration().showLinks) {
return null;
}
return (
}
renderPermissionsLink() {
- if (!this.props.conf.showPermissions) {
+ if (!this.getConfiguration().showPermissions) {
return null;
}
return (
}
renderBackgroundTasksLink() {
- if (!this.props.conf.showBackgroundTasks) {
+ if (!this.getConfiguration().showBackgroundTasks) {
return null;
}
return (
}
renderUpdateKeyLink() {
- if (!this.props.conf.showUpdateKey) {
+ if (!this.getConfiguration().showUpdateKey) {
return null;
}
return (
renderDeletionLink() {
const { qualifier } = this.props.component;
- if (!this.props.conf.showSettings) {
+ if (!this.getConfiguration().showSettings) {
return null;
}
};
renderAdminExtensions() {
- const extensions = this.props.conf.extensions || [];
+ const extensions = this.getConfiguration().extensions || [];
return extensions.map(e => this.renderExtension(e, true));
}
{translate('more')}
<i className="icon-dropdown" />
</a>
- <ul className="dropdown-menu">
- {extensions.map(e => this.renderExtension(e, false))}
- </ul>
+ <ul className="dropdown-menu">{extensions.map(e => this.renderExtension(e, false))}</ul>
</li>
);
}
import * as React from 'react';
import IncrementalBadge from './IncrementalBadge';
import BranchStatus from '../../../../components/common/BranchStatus';
-import { Branch, Component, ComponentConfiguration } from '../../../types';
+import { Branch, Component } from '../../../types';
import Tooltip from '../../../../components/controls/Tooltip';
import PendingIcon from '../../../../components/icons-components/PendingIcon';
import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter';
interface Props {
branch?: Branch;
component: Component;
- conf: ComponentConfiguration;
incremental?: boolean;
isInProgress?: boolean;
isFailed?: boolean;
export default function ComponentNavMeta(props: Props) {
const metaList = [];
- const canSeeBackgroundTasks = props.conf.showBackgroundTasks;
+ const canSeeBackgroundTasks =
+ props.component.configuration != undefined && props.component.configuration.showBackgroundTasks;
const backgroundTasksUrl =
(window as any).baseUrl +
`/project/background_tasks?id=${encodeURIComponent(props.component.key)}`;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 Modal from 'react-modal';
+import { deleteBranch } from '../../../../api/branches';
+import { Branch } from '../../../../app/types';
+import { translate, translateWithParameters } from '../../../../helpers/l10n';
+
+interface Props {
+ branch: Branch;
+ component: string;
+ onClose: () => void;
+ onDelete: () => void;
+}
+
+interface State {
+ loading: boolean;
+}
+
+export default class DeleteBranchModal extends React.PureComponent<Props, State> {
+ mounted: boolean;
+ state: State = { loading: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ this.setState({ loading: true });
+ deleteBranch(this.props.component, this.props.branch.name).then(
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ this.props.onDelete();
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ };
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ event.stopPropagation();
+ this.props.onClose();
+ };
+
+ render() {
+ const { branch } = this.props;
+ const header = translate('branches.delete');
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+ <header className="modal-head">
+ <h2>{header}</h2>
+ </header>
+ <form onSubmit={this.handleSubmit}>
+ <div className="modal-body">
+ {translateWithParameters('branches.delete.are_you_sure', branch.name)}
+ </div>
+ <footer className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ <button className="button-red" disabled={this.state.loading} type="submit">
+ {translate('delete')}
+ </button>
+ <a href="#" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </footer>
+ </form>
+ </Modal>
+ );
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 BubblePopup from '../../../../components/common/BubblePopup';
-import { translate } from '../../../../helpers/l10n';
-
-interface Props {
- popupPosition?: any;
-}
-
-export default function NoBranchSupportPopup(props: Props) {
- return (
- <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom">
- <div className="abs-width-400">
- <h6 className="spacer-bottom">{translate('branches.no_support.header')}</h6>
- <p className="big-spacer-bottom markdown">{translate('branches.no_support.header.text')}</p>
- <p>
- <a href="https://redirect.sonarsource.com/doc/branches.html" target="_blank">
- {translate('learn_more')}
- </a>
- <a
- className="button spacer-left"
- href="https://www.sonarsource.com/company/contact/"
- target="_blank">
- {translate('buy_developer_pack')}
- </a>
- </p>
- </div>
- </BubblePopup>
- );
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 Modal from 'react-modal';
+import { renameBranch } from '../../../../api/branches';
+import { Branch } from '../../../../app/types';
+import { translate } from '../../../../helpers/l10n';
+
+interface Props {
+ branch: Branch;
+ component: string;
+ onClose: () => void;
+ onRename: () => void;
+}
+
+interface State {
+ loading: boolean;
+ name?: string;
+}
+
+export default class RenameBranchModal extends React.PureComponent<Props, State> {
+ mounted: boolean;
+ state: State = { loading: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ if (!this.state.name) {
+ return;
+ }
+ this.setState({ loading: true });
+ renameBranch(this.props.component, this.state.name).then(
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ this.props.onRename();
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ };
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ event.stopPropagation();
+ this.props.onClose();
+ };
+
+ handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ render() {
+ const { branch } = this.props;
+ const header = translate('branches.rename');
+ const submitDisabled =
+ this.state.loading || !this.state.name || this.state.name === branch.name;
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+ <header className="modal-head">
+ <h2>{header}</h2>
+ </header>
+ <form onSubmit={this.handleSubmit}>
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="rename-branch-name">
+ {translate('new_name')}
+ <em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="rename-branch-name"
+ maxLength={100}
+ name="name"
+ onChange={this.handleNameChange}
+ required={true}
+ size={50}
+ type="text"
+ value={this.state.name != undefined ? this.state.name : branch.name}
+ />
+ </div>
+ </div>
+ <footer className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ <button disabled={submitDisabled} type="submit">
+ {translate('rename')}
+ </button>
+ <a href="#" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </footer>
+ </form>
+ </Modal>
+ );
+ }
+}
it('loads status', () => {
getTasksForComponent.mockClear();
- mount(<ComponentNav branches={[]} component={component} conf={{}} location={{}} />);
+ mount(
+ <ComponentNav branches={[]} component={component} location={{}} onBranchesChange={jest.fn()} />
+ );
expect(getTasksForComponent).toBeCalledWith('component');
});
it('renders', () => {
const wrapper = shallow(
- <ComponentNav branches={[]} component={component} conf={{}} location={{}} />
+ <ComponentNav branches={[]} component={component} location={{}} onBranchesChange={jest.fn()} />
);
wrapper.setState({
incremental: true,
shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
)
shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
)
const wrapper = shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
);
const branch: MainBranch = { isMain: true, name: 'master' };
const component = {} as Component;
const wrapper = shallow(
- <ComponentNavBranch branches={[branch]} currentBranch={branch} project={component} />,
+ <ComponentNavBranch
+ branches={[branch]}
+ component={component}
+ currentBranch={branch}
+ onBranchesChange={jest.fn()}
+ />,
{ context: { branchesEnabled: true } }
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(true);
});
-it('renders no branch support popup', () => {
+it('renders nothing when no branch support', () => {
const branch: MainBranch = { isMain: true, name: 'master' };
const component = {} as Component;
const wrapper = shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: false } }
);
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(false);
- click(wrapper.find('a'));
- expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(true);
-});
-
-it('renders nothing on SonarCloud without branch support', () => {
- const branch: MainBranch = { isMain: true, name: 'master' };
- const component = {} as Component;
- const wrapper = shallow(
- <ComponentNavBranch branches={[branch]} currentBranch={branch} project={component} />,
- { context: { branchesEnabled: false, onSonarCloud: true } }
- );
expect(wrapper.type()).toBeNull();
});
shallow(
<ComponentNavBranchesMenu
branches={[mainBranch(), shortBranch('foo'), longBranch('bar'), shortBranch('baz', true)]}
+ component={project}
currentBranch={mainBranch()}
+ onBranchesChange={jest.fn()}
onClose={jest.fn()}
- project={project}
/>
)
).toMatchSnapshot();
const wrapper = shallow(
<ComponentNavBranchesMenu
branches={[mainBranch(), shortBranch('foo'), shortBranch('foobar'), longBranch('bar')]}
+ component={project}
currentBranch={mainBranch()}
+ onBranchesChange={jest.fn()}
onClose={jest.fn()}
- project={project}
/>
);
wrapper.setState({ query: 'bar' });
const wrapper = shallow(
<ComponentNavBranchesMenu
branches={[mainBranch(), shortBranch('foo'), shortBranch('foobar'), longBranch('bar')]}
+ component={project}
currentBranch={mainBranch()}
+ onBranchesChange={jest.fn()}
onClose={jest.fn()}
- project={project}
/>
);
elementKeydown(wrapper.find('input'), 40);
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import ComponentNavBranchesMenuItem from '../ComponentNavBranchesMenuItem';
+import ComponentNavBranchesMenuItem, { Props } from '../ComponentNavBranchesMenuItem';
import { BranchType, MainBranch, ShortLivingBranch, Component } from '../../../../types';
+import { click } from '../../../../../helpers/testUtils';
+
+const component = { key: 'component' } as Component;
+
+const shortBranch: ShortLivingBranch = {
+ isMain: false,
+ mergeBranch: 'master',
+ name: 'foo',
+ status: { bugs: 1, codeSmells: 2, vulnerabilities: 3 },
+ type: BranchType.SHORT
+};
+
+const mainBranch: MainBranch = { isMain: true, name: 'master' };
it('renders main branch', () => {
- const component = { key: 'component' } as Component;
- const mainBranch: MainBranch = { isMain: true, name: 'master' };
- expect(
- shallow(
- <ComponentNavBranchesMenuItem
- branch={mainBranch}
- component={component}
- onSelect={jest.fn()}
- selected={false}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender({ branch: mainBranch })).toMatchSnapshot();
});
it('renders short-living branch', () => {
- const component = { key: 'component' } as Component;
- const shortBranch: ShortLivingBranch = {
- isMain: false,
- mergeBranch: 'master',
- name: 'foo',
- status: { bugs: 1, codeSmells: 2, vulnerabilities: 3 },
- type: BranchType.SHORT
- };
- expect(
- shallow(
- <ComponentNavBranchesMenuItem
- branch={shortBranch}
- component={component}
- onSelect={jest.fn()}
- selected={false}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
it('renders short-living orhpan branch', () => {
- const component = { key: 'component' } as Component;
- const shortBranch: ShortLivingBranch = {
- isMain: false,
- isOrphan: true,
- mergeBranch: 'master',
- name: 'foo',
- status: { bugs: 1, codeSmells: 2, vulnerabilities: 3 },
- type: BranchType.SHORT
- };
- expect(
- shallow(
- <ComponentNavBranchesMenuItem
- branch={shortBranch}
- component={component}
- onSelect={jest.fn()}
- selected={false}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender({ branch: { ...shortBranch, isOrphan: true } })).toMatchSnapshot();
+});
+
+it('renames main branch', () => {
+ const onBranchesChange = jest.fn();
+ const wrapper = shallowRender({ branch: mainBranch, canAdmin: true, onBranchesChange });
+
+ click(wrapper.find('.js-rename'));
+ (wrapper.find('RenameBranchModal').prop('onRename') as Function)();
+ expect(onBranchesChange).toBeCalled();
});
+
+it('deletes short-living branch', () => {
+ const onBranchesChange = jest.fn();
+ const wrapper = shallowRender({ canAdmin: true, onBranchesChange });
+
+ click(wrapper.find('.js-delete'));
+ (wrapper.find('DeleteBranchModal').prop('onDelete') as Function)();
+ expect(onBranchesChange).toBeCalled();
+});
+
+function shallowRender(props?: { [P in keyof Props]?: Props[P] }) {
+ return shallow(
+ <ComponentNavBranchesMenuItem
+ branch={shortBranch}
+ component={component}
+ onBranchesChange={jest.fn()}
+ onSelect={jest.fn()}
+ selected={false}
+ {...props}
+ />
+ );
+}
import * as React from 'react';
import { shallow } from 'enzyme';
import ComponentNavMenu from '../ComponentNavMenu';
-import {
- Component,
- ShortLivingBranch,
- BranchType,
- LongLivingBranch,
- MainBranch
-} from '../../../../types';
+import { ShortLivingBranch, BranchType, LongLivingBranch, MainBranch } from '../../../../types';
-const mainBranch: MainBranch = {
- isMain: true,
- name: 'master'
+const mainBranch: MainBranch = { isMain: true, name: 'master' };
+
+const baseComponent = {
+ breadcrumbs: [],
+ key: 'foo',
+ name: 'foo',
+ organization: 'org',
+ qualifier: 'TRK'
};
it('should work with extensions', () => {
const component = {
- key: 'foo',
- qualifier: 'TRK',
+ ...baseComponent,
+ configuration: { showSettings: true, extensions: [{ key: 'foo', name: 'Foo' }] },
extensions: [{ key: 'component-foo', name: 'ComponentFoo' }]
};
- const conf = {
- showSettings: true,
- extensions: [{ key: 'foo', name: 'Foo' }]
- };
expect(
- shallow(
- <ComponentNavMenu branch={mainBranch} component={component as Component} conf={conf} />,
- { context: { branchesEnabled: true } }
- )
+ shallow(<ComponentNavMenu branch={mainBranch} component={component} />, {
+ context: { branchesEnabled: true }
+ })
).toMatchSnapshot();
});
it('should work with multiple extensions', () => {
const component = {
- key: 'foo',
- qualifier: 'TRK',
+ ...baseComponent,
+ configuration: {
+ showSettings: true,
+ extensions: [{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }]
+ },
extensions: [
{ key: 'component-foo', name: 'ComponentFoo' },
{ key: 'component-bar', name: 'ComponentBar' }
]
};
- const conf = {
- showSettings: true,
- extensions: [{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }]
- };
expect(
- shallow(
- <ComponentNavMenu branch={mainBranch} component={component as Component} conf={conf} />,
- { context: { branchesEnabled: true } }
- )
+ shallow(<ComponentNavMenu branch={mainBranch} component={component} />, {
+ context: { branchesEnabled: true }
+ })
).toMatchSnapshot();
});
name: 'feature',
type: BranchType.SHORT
};
- const component = { key: 'foo', qualifier: 'TRK' } as Component;
- const conf = { showSettings: true };
+ const component = { ...baseComponent, configuration: { showSettings: true } };
expect(
- shallow(<ComponentNavMenu branch={branch} component={component} conf={conf} />, {
+ shallow(<ComponentNavMenu branch={branch} component={component} />, {
context: { branchesEnabled: true }
})
).toMatchSnapshot();
it('should work for long-living branches', () => {
const branch: LongLivingBranch = { isMain: false, name: 'release', type: BranchType.LONG };
- const component = { key: 'foo', qualifier: 'TRK' } as Component;
[true, false].forEach(showSettings =>
expect(
- shallow(<ComponentNavMenu branch={branch} component={component} conf={{ showSettings }} />, {
- context: { branchesEnabled: true }
- })
+ shallow(
+ <ComponentNavMenu
+ branch={branch}
+ component={{ ...baseComponent, configuration: { showSettings } }}
+ />,
+ { context: { branchesEnabled: true } }
+ )
).toMatchSnapshot()
);
});
it('should work for all qualifiers', () => {
['TRK', 'BRC', 'VW', 'SVW', 'APP'].forEach(checkWithQualifier);
expect.assertions(5);
-
function checkWithQualifier(qualifier: string) {
- const component = { key: 'foo', qualifier } as Component;
+ const component = { ...baseComponent, configuration: { showSettings: true }, qualifier };
expect(
- shallow(
- <ComponentNavMenu
- branch={mainBranch}
- component={component}
- conf={{ showSettings: true }}
- />,
- {
- context: { branchesEnabled: true }
- }
- )
+ shallow(<ComponentNavMenu branch={mainBranch} component={component} />, {
+ context: { branchesEnabled: true }
+ })
).toMatchSnapshot();
}
});
function check(incremental: boolean) {
expect(
shallow(
- <ComponentNavMeta
- branch={{} as Branch}
- component={component}
- conf={{}}
- incremental={incremental}
- />
+ <ComponentNavMeta branch={{} as Branch} component={component} incremental={incremental} />
).find('IncrementalBadge')
).toHaveLength(incremental ? 1 : 0);
}
status: { bugs: 0, codeSmells: 2, vulnerabilities: 3 },
type: BranchType.SHORT
};
- expect(
- shallow(<ComponentNavMeta branch={branch} component={component} conf={{}} />)
- ).toMatchSnapshot();
+ expect(shallow(<ComponentNavMeta branch={branch} component={component} />)).toMatchSnapshot();
});
it('renders meta for long-living branch', () => {
status: { qualityGateStatus: 'OK' },
type: BranchType.LONG
};
- expect(
- shallow(<ComponentNavMeta branch={branch} component={component} conf={{}} />)
- ).toMatchSnapshot();
+ expect(shallow(<ComponentNavMeta branch={branch} component={component} />)).toMatchSnapshot();
});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+jest.mock('../../../../../api/branches', () => ({ deleteBranch: jest.fn() }));
+
+import * as React from 'react';
+import { shallow, ShallowWrapper } from 'enzyme';
+import DeleteBranchModal from '../DeleteBranchModal';
+import { ShortLivingBranch, BranchType } from '../../../../../app/types';
+import { submit, doAsync, click } from '../../../../../helpers/testUtils';
+import { deleteBranch } from '../../../../../api/branches';
+
+beforeEach(() => {
+ (deleteBranch as jest.Mock<any>).mockClear();
+});
+
+it('renders', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+ wrapper.setState({ loading: true });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('deletes branch', () => {
+ (deleteBranch as jest.Mock<any>).mockImplementation(() => Promise.resolve());
+ const onDelete = jest.fn();
+ const wrapper = shallowRender(onDelete);
+
+ submitForm(wrapper);
+
+ return doAsync().then(() => {
+ wrapper.update();
+ expect(wrapper.state().loading).toBe(false);
+ expect(onDelete).toBeCalled();
+ expect(deleteBranch).toBeCalledWith('foo', 'feature');
+ });
+});
+
+it('cancels', () => {
+ const onClose = jest.fn();
+ const wrapper = shallowRender(jest.fn(), onClose);
+
+ click(wrapper.find('a'));
+
+ return doAsync().then(() => {
+ expect(onClose).toBeCalled();
+ });
+});
+
+it('stops loading on WS error', () => {
+ (deleteBranch as jest.Mock<any>).mockImplementation(() => Promise.reject(null));
+ const onDelete = jest.fn();
+ const wrapper = shallowRender(onDelete);
+
+ submitForm(wrapper);
+
+ return doAsync().then(() => {
+ wrapper.update();
+ expect(wrapper.state().loading).toBe(false);
+ expect(onDelete).not.toBeCalled();
+ expect(deleteBranch).toBeCalledWith('foo', 'feature');
+ });
+});
+
+function shallowRender(onDelete: () => void = jest.fn(), onClose: () => void = jest.fn()) {
+ const branch: ShortLivingBranch = {
+ isMain: false,
+ name: 'feature',
+ mergeBranch: 'master',
+ type: BranchType.SHORT
+ };
+ const wrapper = shallow(
+ <DeleteBranchModal branch={branch} component="foo" onClose={onClose} onDelete={onDelete} />
+ );
+ (wrapper.instance() as any).mounted = true;
+ return wrapper;
+}
+
+function submitForm(wrapper: ShallowWrapper<any, any>) {
+ submit(wrapper.find('form'));
+ expect(wrapper.state().loading).toBe(true);
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 { shallow } from 'enzyme';
-import NoBranchSupportPopup from '../NoBranchSupportPopup';
-
-it('renders', () => {
- expect(shallow(<NoBranchSupportPopup />)).toMatchSnapshot();
-});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+jest.mock('../../../../../api/branches', () => ({ renameBranch: jest.fn() }));
+
+import * as React from 'react';
+import { shallow, ShallowWrapper } from 'enzyme';
+import RenameBranchModal from '../RenameBranchModal';
+import { MainBranch } from '../../../../../app/types';
+import { submit, doAsync, click, change } from '../../../../../helpers/testUtils';
+import { renameBranch } from '../../../../../api/branches';
+
+beforeEach(() => {
+ (renameBranch as jest.Mock<any>).mockClear();
+});
+
+it('renders', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+ wrapper.setState({ name: 'dev' });
+ expect(wrapper).toMatchSnapshot();
+ wrapper.setState({ loading: true });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('renames branch', () => {
+ (renameBranch as jest.Mock<any>).mockImplementation(() => Promise.resolve());
+ const onRename = jest.fn();
+ const wrapper = shallowRender(onRename);
+
+ fillAndSubmit(wrapper);
+
+ return doAsync().then(() => {
+ wrapper.update();
+ expect(wrapper.state().loading).toBe(false);
+ expect(onRename).toBeCalled();
+ expect(renameBranch).toBeCalledWith('foo', 'dev');
+ });
+});
+
+it('cancels', () => {
+ const onClose = jest.fn();
+ const wrapper = shallowRender(jest.fn(), onClose);
+
+ click(wrapper.find('a'));
+
+ return doAsync().then(() => {
+ expect(onClose).toBeCalled();
+ });
+});
+
+it('stops loading on WS error', () => {
+ (renameBranch as jest.Mock<any>).mockImplementation(() => Promise.reject(null));
+ const onRename = jest.fn();
+ const wrapper = shallowRender(onRename);
+
+ fillAndSubmit(wrapper);
+
+ return doAsync().then(() => {
+ wrapper.update();
+ expect(wrapper.state().loading).toBe(false);
+ expect(onRename).not.toBeCalled();
+ });
+});
+
+function shallowRender(onRename: () => void = jest.fn(), onClose: () => void = jest.fn()) {
+ const branch: MainBranch = { isMain: true, name: 'master' };
+ const wrapper = shallow(
+ <RenameBranchModal branch={branch} component="foo" onClose={onClose} onRename={onRename} />
+ );
+ (wrapper.instance() as any).mounted = true;
+ return wrapper;
+}
+
+function fillAndSubmit(wrapper: ShallowWrapper<any, any>) {
+ change(wrapper.find('input'), 'dev');
+ submit(wrapper.find('form'));
+ expect(wrapper.state().loading).toBe(true);
+}
"qualifier": "TRK",
}
}
- conf={Object {}}
incremental={true}
isFailed={true}
isInProgress={true}
"qualifier": "TRK",
}
}
- conf={Object {}}
location={Object {}}
/>
</ContextNavBar>
</div>
`;
-exports[`renders no branch support popup 1`] = `
-<div
- className="navbar-context-branches"
->
- <BranchIcon
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- className="little-spacer-right"
- color="#cdcdcd"
- />
- <span
- className="note"
- >
- master
- </span>
- <div
- className="display-inline-block spacer-left"
- >
- <a
- className="link-no-underline"
- href="#"
- onClick={[Function]}
- >
- <HelpIcon
- fill="#cdcdcd"
- />
- </a>
- <BubblePopupHelper
- isOpen={false}
- popup={<NoBranchSupportPopup />}
- position="bottomleft"
- togglePopup={[Function]}
- />
- </div>
-</div>
-`;
-
exports[`renders short-living branch 1`] = `
<div
className="navbar-context-branches dropdown"
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={true}
/>
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={true}
/>
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
}
}
>
- <div>
+ <div
+ className="navbar-context-meta-branch-menu-item-name"
+ >
<BranchIcon
branch={
Object {
}
}
>
- <div>
+ <div
+ className="navbar-context-meta-branch-menu-item-name"
+ >
<BranchIcon
branch={
Object {
}
}
>
- <div>
+ <div
+ className="navbar-context-meta-branch-menu-item-name"
+ >
<BranchIcon
branch={
Object {
project_settings.page
</Link>
</li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/branches",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_branches.page
- </Link>
- </li>
<li>
<Link
activeClassName="active"
}
}
>
- layout.settings
- Â
+ branches.branch_settings
</Link>
</li>
</NavBarTabs>
project_settings.page
</Link>
</li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/branches",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_branches.page
- </Link>
- </li>
<li>
<Link
activeClassName="active"
project_settings.page
</Link>
</li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/branches",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_branches.page
- </Link>
- </li>
<li>
<Link
activeClassName="active"
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="branches.delete"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ branches.delete
+ </h2>
+ </header>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-body"
+ >
+ branches.delete.are_you_sure.feature
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ className="button-red"
+ disabled={false}
+ type="submit"
+ >
+ delete
+ </button>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
+
+exports[`renders 2`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="branches.delete"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ branches.delete
+ </h2>
+ </header>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-body"
+ >
+ branches.delete.are_you_sure.feature
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <i
+ className="spinner spacer-right"
+ />
+ <button
+ className="button-red"
+ disabled={true}
+ type="submit"
+ >
+ delete
+ </button>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<BubblePopup
- customClass="bubble-popup-bottom"
->
- <div
- className="abs-width-400"
- >
- <h6
- className="spacer-bottom"
- >
- branches.no_support.header
- </h6>
- <p
- className="big-spacer-bottom markdown"
- >
- branches.no_support.header.text
- </p>
- <p>
- <a
- href="https://redirect.sonarsource.com/doc/branches.html"
- target="_blank"
- >
- learn_more
- </a>
- <a
- className="button spacer-left"
- href="https://www.sonarsource.com/company/contact/"
- target="_blank"
- >
- buy_developer_pack
- </a>
- </p>
- </div>
-</BubblePopup>
-`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="branches.rename"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ branches.rename
+ </h2>
+ </header>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="rename-branch-name"
+ >
+ new_name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="rename-branch-name"
+ maxLength={100}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ size={50}
+ type="text"
+ value="master"
+ />
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ disabled={true}
+ type="submit"
+ >
+ rename
+ </button>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
+
+exports[`renders 2`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="branches.rename"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ branches.rename
+ </h2>
+ </header>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="rename-branch-name"
+ >
+ new_name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="rename-branch-name"
+ maxLength={100}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ size={50}
+ type="text"
+ value="dev"
+ />
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ disabled={false}
+ type="submit"
+ >
+ rename
+ </button>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
+
+exports[`renders 3`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="branches.rename"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ branches.rename
+ </h2>
+ </header>
+ <form
+ onSubmit={[Function]}
+ >
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="rename-branch-name"
+ >
+ new_name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="rename-branch-name"
+ maxLength={100}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ size={50}
+ type="text"
+ value="dev"
+ />
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <i
+ className="spinner spacer-right"
+ />
+ <button
+ disabled={true}
+ type="submit"
+ >
+ rename
+ </button>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
version?: string;
}
-export interface ComponentConfiguration {
+interface ComponentConfiguration {
extensions?: ComponentExtension[];
showBackgroundTasks?: boolean;
showLinks?: boolean;
import portfolioRoutes from '../../apps/portfolio/routes';
import projectActivityRoutes from '../../apps/projectActivity/routes';
import projectAdminRoutes from '../../apps/project-admin/routes';
-import projectBranchesRoutes from '../../apps/projectBranches/routes';
import projectQualityGateRoutes from '../../apps/projectQualityGate/routes';
import projectQualityProfilesRoutes from '../../apps/projectQualityProfiles/routes';
import projectsRoutes from '../../apps/projects/routes';
component={ProjectAdminPageExtension}
/>
<Route path="project/background_tasks" childRoutes={backgroundTasksRoutes} />
- <Route path="project/branches" childRoutes={projectBranchesRoutes} />
<Route path="project/settings" childRoutes={settingsRoutes} />
<Route path="project_roles" childRoutes={projectPermissionsRoutes} />
</Route>
query: { id: this.props.component.key }
});
}
- if (isShortLivingBranch(this.props.branch)) {
+ if (isShortLivingBranch(this.props.branch) && !this.isFile()) {
this.context.router.replace(getProjectBranchUrl(this.props.component.key, this.props.branch));
}
}
isPortfolio() {
- return this.props.component.qualifier === 'VW' || this.props.component.qualifier === 'SVW';
+ return ['VW', 'SVW'].includes(this.props.component.qualifier);
+ }
+
+ isFile() {
+ return ['FIL', 'UTS'].includes(this.props.component.qualifier);
}
render() {
const { branch, component } = this.props;
- if (this.isPortfolio() || isShortLivingBranch(branch)) {
+ if (this.isPortfolio() || (isShortLivingBranch(branch) && !this.isFile())) {
return null;
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 BranchRow from './BranchRow';
-import { Branch } from '../../../app/types';
-import { sortBranchesAsTree } from '../../../helpers/branches';
-import { translate } from '../../../helpers/l10n';
-
-interface Props {
- branches: Branch[];
- component: { key: string };
- onBranchesChange: () => void;
-}
-
-export default function App({ branches, component, onBranchesChange }: Props) {
- return (
- <div className="page page-limited">
- <header className="page-header">
- <h1 className="page-title">{translate('project_branches.page')}</h1>
- </header>
-
- <table className="data zebra zebra-hover">
- <thead>
- <tr>
- <th>{translate('branch')}</th>
- <th className="text-right">{translate('status')}</th>
- <th className="text-right">{translate('actions')}</th>
- </tr>
- </thead>
- <tbody>
- {sortBranchesAsTree(branches).map(branch => (
- <BranchRow
- branch={branch}
- component={component.key}
- key={branch.name}
- onChange={onBranchesChange}
- />
- ))}
- </tbody>
- </table>
- </div>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 { Branch } from '../../../app/types';
-import * as classNames from 'classnames';
-import DeleteBranchModal from './DeleteBranchModal';
-import BranchStatus from '../../../components/common/BranchStatus';
-import BranchIcon from '../../../components/icons-components/BranchIcon';
-import { isShortLivingBranch } from '../../../helpers/branches';
-import ChangeIcon from '../../../components/icons-components/ChangeIcon';
-import DeleteIcon from '../../../components/icons-components/DeleteIcon';
-import { translate } from '../../../helpers/l10n';
-import Tooltip from '../../../components/controls/Tooltip';
-import RenameBranchModal from './RenameBranchModal';
-
-interface Props {
- branch: Branch;
- component: string;
- onChange: () => void;
-}
-
-interface State {
- deleting: boolean;
- renaming: boolean;
-}
-
-export default class BranchRow extends React.PureComponent<Props, State> {
- mounted: boolean;
- state: State = { deleting: false, renaming: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleDeleteClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
- event.preventDefault();
- event.currentTarget.blur();
- this.setState({ deleting: true });
- };
-
- handleDeletingStop = () => {
- this.setState({ deleting: false });
- };
-
- handleRenameClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
- event.preventDefault();
- event.currentTarget.blur();
- this.setState({ renaming: true });
- };
-
- handleChange = () => {
- if (this.mounted) {
- this.setState({ deleting: false, renaming: false });
- this.props.onChange();
- }
- };
-
- handleRenamingStop = () => {
- this.setState({ renaming: false });
- };
-
- render() {
- const { branch, component } = this.props;
-
- return (
- <tr>
- <td>
- <BranchIcon
- branch={branch}
- className={classNames('little-spacer-right', {
- 'big-spacer-left': isShortLivingBranch(branch) && !branch.isOrphan
- })}
- />
- {branch.name}
- {branch.isMain && (
- <div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
- )}
- </td>
- <td className="thin nowrap text-right">
- <BranchStatus branch={branch} />
- </td>
- <td className="thin nowrap text-right">
- {branch.isMain ? (
- <Tooltip overlay={translate('branches.rename')}>
- <a className="js-rename link-no-underline" href="#" onClick={this.handleRenameClick}>
- <ChangeIcon />
- </a>
- </Tooltip>
- ) : (
- <Tooltip overlay={translate('branches.delete')}>
- <a className="js-delete link-no-underline" href="#" onClick={this.handleDeleteClick}>
- <DeleteIcon />
- </a>
- </Tooltip>
- )}
- </td>
-
- {this.state.deleting && (
- <DeleteBranchModal
- branch={branch}
- component={component}
- onClose={this.handleDeletingStop}
- onDelete={this.handleChange}
- />
- )}
-
- {this.state.renaming && (
- <RenameBranchModal
- branch={branch}
- component={component}
- onClose={this.handleRenamingStop}
- onRename={this.handleChange}
- />
- )}
- </tr>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 Modal from 'react-modal';
-import { deleteBranch } from '../../../api/branches';
-import { Branch } from '../../../app/types';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-
-interface Props {
- branch: Branch;
- component: string;
- onClose: () => void;
- onDelete: () => void;
-}
-
-interface State {
- loading: boolean;
-}
-
-export default class DeleteBranchModal extends React.PureComponent<Props, State> {
- mounted: boolean;
- state: State = { loading: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
- event.preventDefault();
- this.setState({ loading: true });
- deleteBranch(this.props.component, this.props.branch.name).then(
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- this.props.onDelete();
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- );
- };
-
- handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
- event.preventDefault();
- this.props.onClose();
- };
-
- render() {
- const { branch } = this.props;
- const header = translate('branches.delete');
-
- return (
- <Modal
- isOpen={true}
- contentLabel={header}
- className="modal"
- overlayClassName="modal-overlay"
- onRequestClose={this.props.onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
- <form onSubmit={this.handleSubmit}>
- <div className="modal-body">
- {translateWithParameters('branches.delete.are_you_sure', branch.name)}
- </div>
- <footer className="modal-foot">
- {this.state.loading && <i className="spinner spacer-right" />}
- <button className="button-red" disabled={this.state.loading} type="submit">
- {translate('delete')}
- </button>
- <a href="#" onClick={this.handleCancelClick}>
- {translate('cancel')}
- </a>
- </footer>
- </form>
- </Modal>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 Modal from 'react-modal';
-import { renameBranch } from '../../../api/branches';
-import { Branch } from '../../../app/types';
-import { translate } from '../../../helpers/l10n';
-
-interface Props {
- branch: Branch;
- component: string;
- onClose: () => void;
- onRename: () => void;
-}
-
-interface State {
- loading: boolean;
- name?: string;
-}
-
-export default class RenameBranchModal extends React.PureComponent<Props, State> {
- mounted: boolean;
- state: State = { loading: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
- event.preventDefault();
- if (!this.state.name) {
- return;
- }
- this.setState({ loading: true });
- renameBranch(this.props.component, this.state.name).then(
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- this.props.onRename();
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- );
- };
-
- handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
- event.preventDefault();
- this.props.onClose();
- };
-
- handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
- this.setState({ name: event.currentTarget.value });
- };
-
- render() {
- const { branch } = this.props;
- const header = translate('branches.rename');
- const submitDisabled =
- this.state.loading || !this.state.name || this.state.name === branch.name;
-
- return (
- <Modal
- isOpen={true}
- contentLabel={header}
- className="modal"
- overlayClassName="modal-overlay"
- onRequestClose={this.props.onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
- <form onSubmit={this.handleSubmit}>
- <div className="modal-body">
- <div className="modal-field">
- <label htmlFor="rename-branch-name">
- {translate('new_name')}
- <em className="mandatory">*</em>
- </label>
- <input
- autoFocus={true}
- id="rename-branch-name"
- maxLength={100}
- name="name"
- onChange={this.handleNameChange}
- required={true}
- size={50}
- type="text"
- value={this.state.name != undefined ? this.state.name : branch.name}
- />
- </div>
- </div>
- <footer className="modal-foot">
- {this.state.loading && <i className="spinner spacer-right" />}
- <button disabled={submitDisabled} type="submit">
- {translate('rename')}
- </button>
- <a href="#" onClick={this.handleCancelClick}>
- {translate('cancel')}
- </a>
- </footer>
- </form>
- </Modal>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 { shallow } from 'enzyme';
-import App from '../App';
-import { Branch, BranchType } from '../../../../app/types';
-
-it('renders sorted list of branches', () => {
- const branches: Branch[] = [
- { isMain: true, name: 'master' },
- { isMain: false, name: 'branch-1.0', type: BranchType.LONG },
- { isMain: false, name: 'branch-1.0', mergeBranch: 'master', type: BranchType.SHORT }
- ];
- expect(
- shallow(<App branches={branches} component={{ key: 'foo' }} onBranchesChange={jest.fn()} />)
- ).toMatchSnapshot();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 { shallow } from 'enzyme';
-import BranchRow from '../BranchRow';
-import { MainBranch, ShortLivingBranch, BranchType } from '../../../../app/types';
-import { click } from '../../../../helpers/testUtils';
-
-const mainBranch: MainBranch = { isMain: true, name: 'master' };
-
-const shortBranch: ShortLivingBranch = {
- isMain: false,
- name: 'feature',
- mergeBranch: 'foo',
- type: BranchType.SHORT
-};
-
-it('renders main branch', () => {
- expect(shallowRender(mainBranch)).toMatchSnapshot();
-});
-
-it('renders short-living branch', () => {
- expect(shallowRender(shortBranch)).toMatchSnapshot();
-});
-
-it('renames main branch', () => {
- const onChange = jest.fn();
- const wrapper = shallowRender(mainBranch, onChange);
-
- click(wrapper.find('.js-rename'));
- (wrapper.find('RenameBranchModal').prop('onRename') as Function)();
- expect(onChange).toBeCalled();
-});
-
-it('deletes short-living branch', () => {
- const onChange = jest.fn();
- const wrapper = shallowRender(shortBranch, onChange);
-
- click(wrapper.find('.js-delete'));
- (wrapper.find('DeleteBranchModal').prop('onDelete') as Function)();
- expect(onChange).toBeCalled();
-});
-
-function shallowRender(branch: MainBranch | ShortLivingBranch, onChange: () => void = jest.fn()) {
- const wrapper = shallow(<BranchRow branch={branch} component="foo" onChange={onChange} />);
- (wrapper.instance() as any).mounted = true;
- return wrapper;
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.
- */
-jest.mock('../../../../api/branches', () => ({ deleteBranch: jest.fn() }));
-
-import * as React from 'react';
-import { shallow, ShallowWrapper } from 'enzyme';
-import DeleteBranchModal from '../DeleteBranchModal';
-import { ShortLivingBranch, BranchType } from '../../../../app/types';
-import { submit, doAsync, click } from '../../../../helpers/testUtils';
-import { deleteBranch } from '../../../../api/branches';
-
-beforeEach(() => {
- (deleteBranch as jest.Mock<any>).mockClear();
-});
-
-it('renders', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
- wrapper.setState({ loading: true });
- expect(wrapper).toMatchSnapshot();
-});
-
-it('deletes branch', () => {
- (deleteBranch as jest.Mock<any>).mockImplementation(() => Promise.resolve());
- const onDelete = jest.fn();
- const wrapper = shallowRender(onDelete);
-
- submitForm(wrapper);
-
- return doAsync().then(() => {
- wrapper.update();
- expect(wrapper.state().loading).toBe(false);
- expect(onDelete).toBeCalled();
- expect(deleteBranch).toBeCalledWith('foo', 'feature');
- });
-});
-
-it('cancels', () => {
- const onClose = jest.fn();
- const wrapper = shallowRender(jest.fn(), onClose);
-
- click(wrapper.find('a'));
-
- return doAsync().then(() => {
- expect(onClose).toBeCalled();
- });
-});
-
-it('stops loading on WS error', () => {
- (deleteBranch as jest.Mock<any>).mockImplementation(() => Promise.reject(null));
- const onDelete = jest.fn();
- const wrapper = shallowRender(onDelete);
-
- submitForm(wrapper);
-
- return doAsync().then(() => {
- wrapper.update();
- expect(wrapper.state().loading).toBe(false);
- expect(onDelete).not.toBeCalled();
- expect(deleteBranch).toBeCalledWith('foo', 'feature');
- });
-});
-
-function shallowRender(onDelete: () => void = jest.fn(), onClose: () => void = jest.fn()) {
- const branch: ShortLivingBranch = {
- isMain: false,
- name: 'feature',
- mergeBranch: 'master',
- type: BranchType.SHORT
- };
- const wrapper = shallow(
- <DeleteBranchModal branch={branch} component="foo" onClose={onClose} onDelete={onDelete} />
- );
- (wrapper.instance() as any).mounted = true;
- return wrapper;
-}
-
-function submitForm(wrapper: ShallowWrapper<any, any>) {
- submit(wrapper.find('form'));
- expect(wrapper.state().loading).toBe(true);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.
- */
-jest.mock('../../../../api/branches', () => ({ renameBranch: jest.fn() }));
-
-import * as React from 'react';
-import { shallow, ShallowWrapper } from 'enzyme';
-import RenameBranchModal from '../RenameBranchModal';
-import { MainBranch } from '../../../../app/types';
-import { submit, doAsync, click, change } from '../../../../helpers/testUtils';
-import { renameBranch } from '../../../../api/branches';
-
-beforeEach(() => {
- (renameBranch as jest.Mock<any>).mockClear();
-});
-
-it('renders', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
- wrapper.setState({ name: 'dev' });
- expect(wrapper).toMatchSnapshot();
- wrapper.setState({ loading: true });
- expect(wrapper).toMatchSnapshot();
-});
-
-it('renames branch', () => {
- (renameBranch as jest.Mock<any>).mockImplementation(() => Promise.resolve());
- const onRename = jest.fn();
- const wrapper = shallowRender(onRename);
-
- fillAndSubmit(wrapper);
-
- return doAsync().then(() => {
- wrapper.update();
- expect(wrapper.state().loading).toBe(false);
- expect(onRename).toBeCalled();
- expect(renameBranch).toBeCalledWith('foo', 'dev');
- });
-});
-
-it('cancels', () => {
- const onClose = jest.fn();
- const wrapper = shallowRender(jest.fn(), onClose);
-
- click(wrapper.find('a'));
-
- return doAsync().then(() => {
- expect(onClose).toBeCalled();
- });
-});
-
-it('stops loading on WS error', () => {
- (renameBranch as jest.Mock<any>).mockImplementation(() => Promise.reject(null));
- const onRename = jest.fn();
- const wrapper = shallowRender(onRename);
-
- fillAndSubmit(wrapper);
-
- return doAsync().then(() => {
- wrapper.update();
- expect(wrapper.state().loading).toBe(false);
- expect(onRename).not.toBeCalled();
- });
-});
-
-function shallowRender(onRename: () => void = jest.fn(), onClose: () => void = jest.fn()) {
- const branch: MainBranch = { isMain: true, name: 'master' };
- const wrapper = shallow(
- <RenameBranchModal branch={branch} component="foo" onClose={onClose} onRename={onRename} />
- );
- (wrapper.instance() as any).mounted = true;
- return wrapper;
-}
-
-function fillAndSubmit(wrapper: ShallowWrapper<any, any>) {
- change(wrapper.find('input'), 'dev');
- submit(wrapper.find('form'));
- expect(wrapper.state().loading).toBe(true);
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders sorted list of branches 1`] = `
-<div
- className="page page-limited"
->
- <header
- className="page-header"
- >
- <h1
- className="page-title"
- >
- project_branches.page
- </h1>
- </header>
- <table
- className="data zebra zebra-hover"
- >
- <thead>
- <tr>
- <th>
- branch
- </th>
- <th
- className="text-right"
- >
- status
- </th>
- <th
- className="text-right"
- >
- actions
- </th>
- </tr>
- </thead>
- <tbody>
- <BranchRow
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- component="foo"
- onChange={[Function]}
- />
- <BranchRow
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "master",
- "name": "branch-1.0",
- "type": "SHORT",
- }
- }
- component="foo"
- onChange={[Function]}
- />
- <BranchRow
- branch={
- Object {
- "isMain": false,
- "name": "branch-1.0",
- "type": "LONG",
- }
- }
- component="foo"
- onChange={[Function]}
- />
- </tbody>
- </table>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders main branch 1`] = `
-<tr>
- <td>
- <BranchIcon
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- className="little-spacer-right"
- />
- master
- <div
- className="outline-badge spacer-left"
- >
- branches.main_branch
- </div>
- </td>
- <td
- className="thin nowrap text-right"
- >
- <BranchStatus
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- />
- </td>
- <td
- className="thin nowrap text-right"
- >
- <Tooltip
- overlay="branches.rename"
- placement="bottom"
- >
- <a
- className="js-rename link-no-underline"
- href="#"
- onClick={[Function]}
- >
- <ChangeIcon />
- </a>
- </Tooltip>
- </td>
-</tr>
-`;
-
-exports[`renders short-living branch 1`] = `
-<tr>
- <td>
- <BranchIcon
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "foo",
- "name": "feature",
- "type": "SHORT",
- }
- }
- className="little-spacer-right big-spacer-left"
- />
- feature
- </td>
- <td
- className="thin nowrap text-right"
- >
- <BranchStatus
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "foo",
- "name": "feature",
- "type": "SHORT",
- }
- }
- />
- </td>
- <td
- className="thin nowrap text-right"
- >
- <Tooltip
- overlay="branches.delete"
- placement="bottom"
- >
- <a
- className="js-delete link-no-underline"
- href="#"
- onClick={[Function]}
- >
- <DeleteIcon />
- </a>
- </Tooltip>
- </td>
-</tr>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<Modal
- ariaHideApp={true}
- bodyOpenClassName="ReactModal__Body--open"
- className="modal"
- closeTimeoutMS={0}
- contentLabel="branches.delete"
- isOpen={true}
- onRequestClose={[Function]}
- overlayClassName="modal-overlay"
- parentSelector={[Function]}
- portalClassName="ReactModalPortal"
- shouldCloseOnOverlayClick={true}
->
- <header
- className="modal-head"
- >
- <h2>
- branches.delete
- </h2>
- </header>
- <form
- onSubmit={[Function]}
- >
- <div
- className="modal-body"
- >
- branches.delete.are_you_sure.feature
- </div>
- <footer
- className="modal-foot"
- >
- <button
- className="button-red"
- disabled={false}
- type="submit"
- >
- delete
- </button>
- <a
- href="#"
- onClick={[Function]}
- >
- cancel
- </a>
- </footer>
- </form>
-</Modal>
-`;
-
-exports[`renders 2`] = `
-<Modal
- ariaHideApp={true}
- bodyOpenClassName="ReactModal__Body--open"
- className="modal"
- closeTimeoutMS={0}
- contentLabel="branches.delete"
- isOpen={true}
- onRequestClose={[Function]}
- overlayClassName="modal-overlay"
- parentSelector={[Function]}
- portalClassName="ReactModalPortal"
- shouldCloseOnOverlayClick={true}
->
- <header
- className="modal-head"
- >
- <h2>
- branches.delete
- </h2>
- </header>
- <form
- onSubmit={[Function]}
- >
- <div
- className="modal-body"
- >
- branches.delete.are_you_sure.feature
- </div>
- <footer
- className="modal-foot"
- >
- <i
- className="spinner spacer-right"
- />
- <button
- className="button-red"
- disabled={true}
- type="submit"
- >
- delete
- </button>
- <a
- href="#"
- onClick={[Function]}
- >
- cancel
- </a>
- </footer>
- </form>
-</Modal>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<Modal
- ariaHideApp={true}
- bodyOpenClassName="ReactModal__Body--open"
- className="modal"
- closeTimeoutMS={0}
- contentLabel="branches.rename"
- isOpen={true}
- onRequestClose={[Function]}
- overlayClassName="modal-overlay"
- parentSelector={[Function]}
- portalClassName="ReactModalPortal"
- shouldCloseOnOverlayClick={true}
->
- <header
- className="modal-head"
- >
- <h2>
- branches.rename
- </h2>
- </header>
- <form
- onSubmit={[Function]}
- >
- <div
- className="modal-body"
- >
- <div
- className="modal-field"
- >
- <label
- htmlFor="rename-branch-name"
- >
- new_name
- <em
- className="mandatory"
- >
- *
- </em>
- </label>
- <input
- autoFocus={true}
- id="rename-branch-name"
- maxLength={100}
- name="name"
- onChange={[Function]}
- required={true}
- size={50}
- type="text"
- value="master"
- />
- </div>
- </div>
- <footer
- className="modal-foot"
- >
- <button
- disabled={true}
- type="submit"
- >
- rename
- </button>
- <a
- href="#"
- onClick={[Function]}
- >
- cancel
- </a>
- </footer>
- </form>
-</Modal>
-`;
-
-exports[`renders 2`] = `
-<Modal
- ariaHideApp={true}
- bodyOpenClassName="ReactModal__Body--open"
- className="modal"
- closeTimeoutMS={0}
- contentLabel="branches.rename"
- isOpen={true}
- onRequestClose={[Function]}
- overlayClassName="modal-overlay"
- parentSelector={[Function]}
- portalClassName="ReactModalPortal"
- shouldCloseOnOverlayClick={true}
->
- <header
- className="modal-head"
- >
- <h2>
- branches.rename
- </h2>
- </header>
- <form
- onSubmit={[Function]}
- >
- <div
- className="modal-body"
- >
- <div
- className="modal-field"
- >
- <label
- htmlFor="rename-branch-name"
- >
- new_name
- <em
- className="mandatory"
- >
- *
- </em>
- </label>
- <input
- autoFocus={true}
- id="rename-branch-name"
- maxLength={100}
- name="name"
- onChange={[Function]}
- required={true}
- size={50}
- type="text"
- value="dev"
- />
- </div>
- </div>
- <footer
- className="modal-foot"
- >
- <button
- disabled={false}
- type="submit"
- >
- rename
- </button>
- <a
- href="#"
- onClick={[Function]}
- >
- cancel
- </a>
- </footer>
- </form>
-</Modal>
-`;
-
-exports[`renders 3`] = `
-<Modal
- ariaHideApp={true}
- bodyOpenClassName="ReactModal__Body--open"
- className="modal"
- closeTimeoutMS={0}
- contentLabel="branches.rename"
- isOpen={true}
- onRequestClose={[Function]}
- overlayClassName="modal-overlay"
- parentSelector={[Function]}
- portalClassName="ReactModalPortal"
- shouldCloseOnOverlayClick={true}
->
- <header
- className="modal-head"
- >
- <h2>
- branches.rename
- </h2>
- </header>
- <form
- onSubmit={[Function]}
- >
- <div
- className="modal-body"
- >
- <div
- className="modal-field"
- >
- <label
- htmlFor="rename-branch-name"
- >
- new_name
- <em
- className="mandatory"
- >
- *
- </em>
- </label>
- <input
- autoFocus={true}
- id="rename-branch-name"
- maxLength={100}
- name="name"
- onChange={[Function]}
- required={true}
- size={50}
- type="text"
- value="dev"
- />
- </div>
- </div>
- <footer
- className="modal-foot"
- >
- <i
- className="spinner spacer-right"
- />
- <button
- disabled={true}
- type="submit"
- >
- rename
- </button>
- <a
- href="#"
- onClick={[Function]}
- >
- cancel
- </a>
- </footer>
- </form>
-</Modal>
-`;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 { RouterState, IndexRouteProps } from 'react-router';
-
-const routes = [
- {
- getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) {
- import('./components/App').then(i => callback(null, { component: (i as any).default }));
- }
- }
-];
-
-export default routes;
}
interface State {
+ advanced: boolean;
branch: string;
createdProject?: { key: string; name: string };
key: string;
constructor(props: Props) {
super(props);
this.state = {
+ advanced: false,
branch: '',
key: '',
loading: false,
this.props.onClose();
};
+ handleAdvancedClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState(state => ({ advanced: !state.advanced }));
+ };
+
handleInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
const { name, value } = event.currentTarget;
this.setState({ [name]: value });
value={this.state.name}
/>
</div>
- <div className="modal-field">
- <label htmlFor="create-project-branch">{translate('branch')}</label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={this.handleInputChange}
- type="text"
- value={this.state.branch}
- />
- </div>
<div className="modal-field">
<label htmlFor="create-project-key">
{translate('key')}
</div>
)}
</div>
+ {this.state.advanced ? (
+ <div className="modal-field big-spacer-top">
+ <label htmlFor="create-project-branch">{translate('branch')}</label>
+ <input
+ autoFocus={true}
+ id="create-project-branch"
+ maxLength={200}
+ name="branch"
+ onChange={this.handleInputChange}
+ type="text"
+ value={this.state.branch}
+ />
+ </div>
+ ) : (
+ <div className="modal-field big-spacer-top">
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={this.handleAdvancedClick}>
+ {translate('more')}
+ </a>
+ </div>
+ )}
</div>
<footer className="modal-foot">
import * as React from 'react';
import { shallow } from 'enzyme';
import CreateProjectForm from '../CreateProjectForm';
-import { change, submit } from '../../../helpers/testUtils';
+import { change, submit, click } from '../../../helpers/testUtils';
const createProject = require('../../../api/components').createProject as jest.Mock<any>;
change(wrapper.find('input[name="name"]'), 'name', {
currentTarget: { name: 'name', value: 'name' }
});
- change(wrapper.find('input[name="branch"]'), 'branch', {
- currentTarget: { name: 'branch', value: 'branch' }
- });
change(wrapper.find('input[name="key"]'), 'key', {
currentTarget: { name: 'key', value: 'key' }
});
submit(wrapper.find('form'));
expect(createProject).toBeCalledWith({
- branch: 'branch',
+ branch: '',
name: 'name',
organization: 'org',
project: 'key',
});
expect(wrapper).toMatchSnapshot();
- await new Promise(resolve => setImmediate(resolve));
+ await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
+
+it('shows more', () => {
+ const wrapper = shallow(
+ <CreateProjectForm
+ onClose={jest.fn()}
+ onProjectCreated={jest.fn()}
+ organization={organization}
+ />
+ );
+ expect(wrapper).toMatchSnapshot();
+
+ click(wrapper.find('.js-more'));
+ expect(wrapper).toMatchSnapshot();
+});
value=""
/>
</div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="create-project-branch"
- >
- branch
- </label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={[Function]}
- type="text"
- value=""
- />
- </div>
<div
className="modal-field"
>
/>
</div>
</div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
</div>
<footer
className="modal-foot"
value="name"
/>
</div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="create-project-branch"
- >
- branch
- </label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={[Function]}
- type="text"
- value="branch"
- />
- </div>
<div
className="modal-field"
>
/>
</div>
</div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
</div>
<footer
className="modal-foot"
value="name"
/>
</div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="create-project-branch"
- >
- branch
- </label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={[Function]}
- type="text"
- value="branch"
- />
- </div>
<div
className="modal-field"
>
/>
</div>
</div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
</div>
<footer
className="modal-foot"
</div>
</Modal>
`;
+
+exports[`shows more 1`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="modal form"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <form
+ id="create-project-form"
+ onSubmit={[Function]}
+ >
+ <header
+ className="modal-head"
+ >
+ <h2>
+ qualifiers.create.TRK
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-name"
+ >
+ name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="create-project-name"
+ maxLength={2000}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-key"
+ >
+ key
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ id="create-project-key"
+ maxLength={400}
+ name="key"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label>
+ visibility
+ </label>
+ <VisibilitySelector
+ className="little-spacer-top"
+ onChange={[Function]}
+ visibility="public"
+ />
+ <div
+ className="spacer-top"
+ >
+ <UpgradeOrganizationBox
+ organization="org"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ disabled={false}
+ id="create-project-submit"
+ type="submit"
+ >
+ create
+ </button>
+ <a
+ href="#"
+ id="create-project-cancel"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
+
+exports[`shows more 2`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="modal form"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <form
+ id="create-project-form"
+ onSubmit={[Function]}
+ >
+ <header
+ className="modal-head"
+ >
+ <h2>
+ qualifiers.create.TRK
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-name"
+ >
+ name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="create-project-name"
+ maxLength={2000}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-key"
+ >
+ key
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ id="create-project-key"
+ maxLength={400}
+ name="key"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label>
+ visibility
+ </label>
+ <VisibilitySelector
+ className="little-spacer-top"
+ onChange={[Function]}
+ visibility="public"
+ />
+ <div
+ className="spacer-top"
+ >
+ <UpgradeOrganizationBox
+ organization="org"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <label
+ htmlFor="create-project-branch"
+ >
+ branch
+ </label>
+ <input
+ autoFocus={true}
+ id="create-project-branch"
+ maxLength={200}
+ name="branch"
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ disabled={false}
+ id="create-project-submit"
+ type="submit"
+ >
+ create
+ </button>
+ <a
+ href="#"
+ id="create-project-cancel"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
link: (
<Link
to={{
- pathname: '/project/branches',
+ pathname: '/project/settings',
query: { id: this.props.component && this.props.component.key }
}}>
{translate('branches.settings_hint_tab')}
const totalIssues =
branch.status.bugs + branch.status.vulnerabilities + branch.status.codeSmells;
- return (
+ const indicatorClassName = classNames('branch-status-indicator', {
+ 'is-failed': totalIssues > 0,
+ 'is-passed': totalIssues === 0
+ });
+
+ return concise ? (
<ul className="list-inline branch-status">
+ <li>{totalIssues}</li>
+ <li className="spacer-left">
+ <i className={indicatorClassName} />
+ </li>
+ </ul>
+ ) : (
+ <ul className="list-inline branch-status">
+ <li className="spacer-right">
+ <i className={indicatorClassName} />
+ </li>
+ <li>
+ {branch.status.bugs}
+ <BugIcon />
+ </li>
+ <li>
+ {branch.status.vulnerabilities}
+ <VulnerabilityIcon />
+ </li>
<li>
- <i
- className={classNames('branch-status-indicator', {
- 'is-failed': totalIssues > 0,
- 'is-passed': totalIssues === 0
- })}
- />
+ {branch.status.codeSmells}
+ <CodeSmellIcon />
</li>
- {concise && <li>{totalIssues}</li>}
- {!concise && (
- <li>
- {branch.status.bugs}
- <BugIcon className="little-spacer-left" />
- </li>
- )}
- {!concise && (
- <li>
- {branch.status.vulnerabilities}
- <VulnerabilityIcon className="little-spacer-left" />
- </li>
- )}
- {!concise && (
- <li>
- {branch.status.codeSmells}
- <CodeSmellIcon className="little-spacer-left" />
- </li>
- )}
</ul>
);
} else {
<ul
className="list-inline branch-status"
>
- <li>
+ <li
+ className="spacer-right"
+ >
<i
className="branch-status-indicator is-passed"
/>
</li>
<li>
0
- <BugIcon
- className="little-spacer-left"
- />
+ <BugIcon />
</li>
<li>
0
- <VulnerabilityIcon
- className="little-spacer-left"
- />
+ <VulnerabilityIcon />
</li>
<li>
0
- <CodeSmellIcon
- className="little-spacer-left"
- />
+ <CodeSmellIcon />
</li>
</ul>
`;
<ul
className="list-inline branch-status"
>
- <li>
+ <li
+ className="spacer-right"
+ >
<i
className="branch-status-indicator is-failed"
/>
</li>
<li>
0
- <BugIcon
- className="little-spacer-left"
- />
+ <BugIcon />
</li>
<li>
0
- <VulnerabilityIcon
- className="little-spacer-left"
- />
+ <VulnerabilityIcon />
</li>
<li>
1
- <CodeSmellIcon
- className="little-spacer-left"
- />
+ <CodeSmellIcon />
</li>
</ul>
`;
<ul
className="list-inline branch-status"
>
- <li>
+ <li
+ className="spacer-right"
+ >
<i
className="branch-status-indicator is-failed"
/>
</li>
<li>
7
- <BugIcon
- className="little-spacer-left"
- />
+ <BugIcon />
</li>
<li>
6
- <VulnerabilityIcon
- className="little-spacer-left"
- />
+ <VulnerabilityIcon />
</li>
<li>
3
- <CodeSmellIcon
- className="little-spacer-left"
- />
+ <CodeSmellIcon />
</li>
</ul>
`;
branches.delete=Delete Branch
branches.delete.are_you_sure=Are you sure you want to delete branch "{0}"?
branches.rename=Rename Branch
-branches.manage=Manage branches
branches.orphan_branch=Orphan Branch
branches.orphan_branches=Orphan Branches
branches.orphan_branches.tooltip=When a target branch of a short-living branch was deleted, this short-living branch becomes orphan.
branches.main_branch=Main Branch
-branches.settings_hint=To administrate your branches, you have to go to your main branch's {link} tab.
-branches.settings_hint_tab=Administration > Branches
+branches.branch_settings=Branch Settings
+branches.settings_hint=To administrate your project, you have to go to your main branch's {link} tab.
+branches.settings_hint_tab=Administration
#------------------------------------------------------------------------------