@@ -267,7 +267,7 @@ export class ComponentNavMenu extends React.PureComponent<Props> { | |||
<Link | |||
activeClassName="active" | |||
to={{ pathname: '/project/branches', query: { id: this.props.component.key } }}> | |||
{translate('project_branches.page')} | |||
{translate('project_branch_pull_request.page')} | |||
</Link> | |||
</li> | |||
); |
@@ -161,7 +161,7 @@ exports[`should work for all qualifiers 1`] = ` | |||
} | |||
} | |||
> | |||
project_branches.page | |||
project_branch_pull_request.page | |||
</Link> | |||
</li> | |||
<li> | |||
@@ -902,7 +902,7 @@ exports[`should work with extensions 2`] = ` | |||
} | |||
} | |||
> | |||
project_branches.page | |||
project_branch_pull_request.page | |||
</Link> | |||
</li> | |||
<li> | |||
@@ -1071,7 +1071,7 @@ exports[`should work with multiple extensions 2`] = ` | |||
} | |||
} | |||
> | |||
project_branches.page | |||
project_branch_pull_request.page | |||
</Link> | |||
</li> | |||
<li> |
@@ -18,163 +18,33 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Link } from 'react-router'; | |||
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { formatMeasure } from 'sonar-ui-common/helpers/measures'; | |||
import { getValues } from '../../../api/settings'; | |||
import { | |||
getBranchLikeKey, | |||
isPullRequest, | |||
isShortLivingBranch, | |||
sortBranchesAsTree | |||
} from '../../../helpers/branches'; | |||
import BranchRow from './BranchRow'; | |||
import BranchLikeTabs from './BranchLikeTabs'; | |||
import LifetimeInformation from './LifetimeInformation'; | |||
interface Props { | |||
export interface AppProps { | |||
branchLikes: T.BranchLike[]; | |||
canAdmin?: boolean; | |||
component: { key: string }; | |||
component: T.Component; | |||
onBranchesChange: () => void; | |||
} | |||
interface State { | |||
branchLifeTime?: string; | |||
loading: boolean; | |||
export function App(props: AppProps) { | |||
const { branchLikes, component, onBranchesChange } = props; | |||
return ( | |||
<div className="page page-limited"> | |||
<header className="page-header"> | |||
<h1>{translate('project_branch_pull_request.page')}</h1> | |||
<LifetimeInformation /> | |||
</header> | |||
<BranchLikeTabs | |||
branchLikes={branchLikes} | |||
component={component} | |||
onBranchesChange={onBranchesChange} | |||
/> | |||
</div> | |||
); | |||
} | |||
const BRANCH_LIFETIME_SETTING = 'sonar.dbcleaner.daysBeforeDeletingInactiveShortLivingBranches'; | |||
export default class App extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { loading: true }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
this.fetchPurgeSetting(); | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
fetchPurgeSetting() { | |||
this.setState({ loading: true }); | |||
getValues({ keys: BRANCH_LIFETIME_SETTING }).then( | |||
settings => { | |||
if (this.mounted) { | |||
this.setState({ | |||
loading: false, | |||
branchLifeTime: settings.length > 0 ? settings[0].value : undefined | |||
}); | |||
} | |||
}, | |||
() => { | |||
this.setState({ loading: false }); | |||
} | |||
); | |||
} | |||
isOrphan = (branchLike: T.BranchLike) => { | |||
return (isShortLivingBranch(branchLike) || isPullRequest(branchLike)) && branchLike.isOrphan; | |||
}; | |||
renderBranchLifeTime() { | |||
const { branchLifeTime } = this.state; | |||
if (!branchLifeTime) { | |||
return null; | |||
} | |||
return ( | |||
<p className="page-description"> | |||
<FormattedMessage | |||
defaultMessage={translate('project_branches.page.life_time')} | |||
id="project_branches.page.life_time" | |||
values={{ days: formatMeasure(this.state.branchLifeTime, 'INT') }} | |||
/> | |||
{this.props.canAdmin && ( | |||
<> | |||
<br /> | |||
<FormattedMessage | |||
defaultMessage={translate('project_branches.page.life_time.admin')} | |||
id="project_branches.page.life_time.admin" | |||
values={{ settings: <Link to="/admin/settings">{translate('settings.page')}</Link> }} | |||
/> | |||
</> | |||
)} | |||
</p> | |||
); | |||
} | |||
render() { | |||
const { branchLikes, component, onBranchesChange } = this.props; | |||
if (this.state.loading) { | |||
return ( | |||
<div className="page page-limited"> | |||
<header className="page-header"> | |||
<h1 className="page-title">{translate('project_branches.page')}</h1> | |||
</header> | |||
<i className="spinner" /> | |||
</div> | |||
); | |||
} | |||
return ( | |||
<div className="page page-limited"> | |||
<header className="page-header"> | |||
<h1 className="page-title">{translate('project_branches.page')}</h1> | |||
<p className="page-description">{translate('project_branches.page.description')}</p> | |||
{this.renderBranchLifeTime()} | |||
</header> | |||
<div className="boxed-group boxed-group-inner"> | |||
<table className="data zebra zebra-hover"> | |||
<thead> | |||
<tr> | |||
<th>{translate('branch')}</th> | |||
<th className="thin nowrap">{translate('status')}</th> | |||
<th className="thin nowrap text-right big-spacer-left"> | |||
{translate('branches.last_analysis_date')} | |||
</th> | |||
<th className="thin nowrap text-right">{translate('actions')}</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{sortBranchesAsTree(branchLikes).map((branchLike, index) => { | |||
const isOrphan = this.isOrphan(branchLike); | |||
const previous = index > 0 ? branchLikes[index - 1] : undefined; | |||
const isPreviousOrphan = previous !== undefined && this.isOrphan(previous); | |||
const showOrphanHeader = isOrphan && !isPreviousOrphan; | |||
return ( | |||
<React.Fragment key={getBranchLikeKey(branchLike)}> | |||
{showOrphanHeader && ( | |||
<tr> | |||
<td colSpan={4}> | |||
<div className="display-inline-block text-middle"> | |||
{translate('branches.orphan_branches')} | |||
</div> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={translate('branches.orphan_branches.tooltip')} | |||
/> | |||
</td> | |||
</tr> | |||
)} | |||
<BranchRow | |||
branchLike={branchLike} | |||
component={component.key} | |||
isOrphan={isOrphan} | |||
onChange={onBranchesChange} | |||
/> | |||
</React.Fragment> | |||
); | |||
})} | |||
</tbody> | |||
</table> | |||
</div> | |||
</div> | |||
); | |||
} | |||
} | |||
export default React.memo(App); |
@@ -0,0 +1,77 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import ActionsDropdown, { | |||
ActionsDropdownItem | |||
} from 'sonar-ui-common/components/controls/ActionsDropdown'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import BranchStatus from '../../../components/common/BranchStatus'; | |||
import BranchIcon from '../../../components/icons-components/BranchIcon'; | |||
import DateFromNow from '../../../components/intl/DateFromNow'; | |||
import { getBranchLikeDisplayName, isMainBranch, isPullRequest } from '../../../helpers/branches'; | |||
export interface BranchLikeRowRendererProps { | |||
branchLike: T.BranchLike; | |||
component: T.Component; | |||
onDelete: () => void; | |||
onRename: () => void; | |||
} | |||
export function BranchLikeRowRenderer(props: BranchLikeRowRendererProps) { | |||
const { branchLike, component, onDelete, onRename } = props; | |||
return ( | |||
<tr> | |||
<td> | |||
<BranchIcon branchLike={branchLike} className="little-spacer-right" /> | |||
{getBranchLikeDisplayName(branchLike)} | |||
{isMainBranch(branchLike) && ( | |||
<div className="badge spacer-left">{translate('branches.main_branch')}</div> | |||
)} | |||
</td> | |||
<td className="thin nowrap"> | |||
<BranchStatus branchLike={branchLike} component={component.key} /> | |||
</td> | |||
<td className="thin nowrap text-right big-spacer-left"> | |||
{branchLike.analysisDate && <DateFromNow date={branchLike.analysisDate} />} | |||
</td> | |||
<td className="thin nowrap text-right"> | |||
<ActionsDropdown className="big-spacer-left"> | |||
{isMainBranch(branchLike) ? ( | |||
<ActionsDropdownItem className="js-rename" onClick={onRename}> | |||
{translate('project_branch_pull_request.branch.rename')} | |||
</ActionsDropdownItem> | |||
) : ( | |||
<ActionsDropdownItem className="js-delete" destructive={true} onClick={onDelete}> | |||
{translate( | |||
isPullRequest(branchLike) | |||
? 'project_branch_pull_request.pull_request.delete' | |||
: 'project_branch_pull_request.branch.delete' | |||
)} | |||
</ActionsDropdownItem> | |||
)} | |||
</ActionsDropdown> | |||
</td> | |||
</tr> | |||
); | |||
} | |||
export default React.memo(BranchLikeRowRenderer); |
@@ -0,0 +1,66 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { getBranchLikeKey } from '../../../helpers/branches'; | |||
import BranchLikeRowRenderer from './BranchLikeRowRenderer'; | |||
export interface BranchLikeTableRendererProps { | |||
component: T.Component; | |||
tableTitle: string; | |||
branchLikes: T.BranchLike[]; | |||
onDelete: (branchLike: T.BranchLike) => void; | |||
onRename: (branchLike: T.BranchLike) => void; | |||
} | |||
export function BranchLikeTableRenderer(props: BranchLikeTableRendererProps) { | |||
const { branchLikes, component, onDelete, onRename, tableTitle } = props; | |||
return ( | |||
<div className="boxed-group boxed-group-inner"> | |||
<table className="data zebra zebra-hover"> | |||
<thead> | |||
<tr> | |||
<th>{tableTitle}</th> | |||
<th className="thin nowrap">{translate('status')}</th> | |||
<th className="thin nowrap text-right big-spacer-left"> | |||
{translate('branches.last_analysis_date')} | |||
</th> | |||
<th className="thin nowrap text-right">{translate('actions')}</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{branchLikes.map(branchLike => ( | |||
<BranchLikeRowRenderer | |||
branchLike={branchLike} | |||
component={component} | |||
key={getBranchLikeKey(branchLike)} | |||
onDelete={() => onDelete(branchLike)} | |||
onRename={() => onRename(branchLike)} | |||
/> | |||
))} | |||
</tbody> | |||
</table> | |||
</div> | |||
); | |||
} | |||
export default React.memo(BranchLikeTableRenderer); |
@@ -0,0 +1,138 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; | |||
import PullRequestIcon from 'sonar-ui-common/components/icons/PullRequestIcon'; | |||
import ShortLivingBranchIcon from 'sonar-ui-common/components/icons/ShortLivingBranchIcon'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { isBranch, isMainBranch, isPullRequest, sortBranches } from '../../../helpers/branches'; | |||
import BranchLikeTableRenderer from './BranchLikeTableRenderer'; | |||
import DeleteBranchModal from './DeleteBranchModal'; | |||
import RenameBranchModal from './RenameBranchModal'; | |||
interface Props { | |||
branchLikes: T.BranchLike[]; | |||
component: T.Component; | |||
onBranchesChange: () => void; | |||
} | |||
interface State { | |||
currentTab: Tabs; | |||
deleting?: T.BranchLike; | |||
renaming?: T.BranchLike; | |||
} | |||
export enum Tabs { | |||
Branch, | |||
PullRequest | |||
} | |||
const TABS = [ | |||
{ | |||
key: Tabs.Branch, | |||
label: ( | |||
<> | |||
<ShortLivingBranchIcon /> | |||
<span className="spacer-left"> | |||
{translate('project_branch_pull_request.tabs.branches')} | |||
</span> | |||
</> | |||
) | |||
}, | |||
{ | |||
key: Tabs.PullRequest, | |||
label: ( | |||
<> | |||
<PullRequestIcon /> | |||
<span className="spacer-left"> | |||
{translate('project_branch_pull_request.tabs.pull_requests')} | |||
</span> | |||
</> | |||
) | |||
} | |||
]; | |||
export default class BranchLikeTabs extends React.PureComponent<Props, State> { | |||
state: State = { currentTab: Tabs.Branch }; | |||
onTabSelect = (currentTab: Tabs) => { | |||
this.setState({ currentTab }); | |||
}; | |||
onDeleteBranchLike = (branchLike: T.BranchLike) => this.setState({ deleting: branchLike }); | |||
onRenameBranchLike = (branchLike: T.BranchLike) => this.setState({ renaming: branchLike }); | |||
onClose = () => this.setState({ deleting: undefined, renaming: undefined }); | |||
onModalActionFulfilled = () => { | |||
this.onClose(); | |||
this.props.onBranchesChange(); | |||
}; | |||
render() { | |||
const { branchLikes, component } = this.props; | |||
const { currentTab, deleting, renaming } = this.state; | |||
let tableTitle = ''; | |||
let branchLikesToDisplay: T.BranchLike[] = []; | |||
if (currentTab === Tabs.Branch) { | |||
tableTitle = translate('project_branch_pull_request.table.branch'); | |||
branchLikesToDisplay = sortBranches(branchLikes.filter(isBranch)); | |||
} else if (currentTab === Tabs.PullRequest) { | |||
tableTitle = translate('project_branch_pull_request.table.pull_request'); | |||
branchLikesToDisplay = branchLikes.filter(isPullRequest); | |||
} | |||
return ( | |||
<> | |||
<BoxedTabs onSelect={this.onTabSelect} selected={currentTab} tabs={TABS} /> | |||
<BranchLikeTableRenderer | |||
branchLikes={branchLikesToDisplay} | |||
component={component} | |||
onDelete={this.onDeleteBranchLike} | |||
onRename={this.onRenameBranchLike} | |||
tableTitle={tableTitle} | |||
/> | |||
{deleting && ( | |||
<DeleteBranchModal | |||
branchLike={deleting} | |||
component={component} | |||
onClose={this.onClose} | |||
onDelete={this.onModalActionFulfilled} | |||
/> | |||
)} | |||
{renaming && isMainBranch(renaming) && ( | |||
<RenameBranchModal | |||
branch={renaming} | |||
component={component} | |||
onClose={this.onClose} | |||
onRename={this.onModalActionFulfilled} | |||
/> | |||
)} | |||
</> | |||
); | |||
} | |||
} |
@@ -1,153 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as classNames from 'classnames'; | |||
import * as React from 'react'; | |||
import ActionsDropdown, { | |||
ActionsDropdownItem | |||
} from 'sonar-ui-common/components/controls/ActionsDropdown'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import BranchStatus from '../../../components/common/BranchStatus'; | |||
import BranchIcon from '../../../components/icons-components/BranchIcon'; | |||
import DateFromNow from '../../../components/intl/DateFromNow'; | |||
import { | |||
getBranchLikeDisplayName, | |||
isMainBranch, | |||
isPullRequest, | |||
isShortLivingBranch | |||
} from '../../../helpers/branches'; | |||
import DeleteBranchModal from './DeleteBranchModal'; | |||
import RenameBranchModal from './RenameBranchModal'; | |||
interface Props { | |||
branchLike: T.BranchLike; | |||
component: string; | |||
isOrphan?: boolean; | |||
onChange: () => void; | |||
} | |||
interface State { | |||
deleting: boolean; | |||
renaming: boolean; | |||
} | |||
export default class BranchRow extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { deleting: false, renaming: false }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
handleDeleteClick = () => { | |||
this.setState({ deleting: true }); | |||
}; | |||
handleDeletingStop = () => { | |||
this.setState({ deleting: false }); | |||
}; | |||
handleRenameClick = () => { | |||
this.setState({ renaming: true }); | |||
}; | |||
handleChange = () => { | |||
if (this.mounted) { | |||
this.setState({ deleting: false, renaming: false }); | |||
this.props.onChange(); | |||
} | |||
}; | |||
handleRenamingStop = () => { | |||
this.setState({ renaming: false }); | |||
}; | |||
renderActions() { | |||
const { branchLike, component } = this.props; | |||
return ( | |||
<td className="thin nowrap text-right"> | |||
<ActionsDropdown className="ig-spacer-left"> | |||
{isMainBranch(branchLike) ? ( | |||
<ActionsDropdownItem className="js-rename" onClick={this.handleRenameClick}> | |||
{translate('branches.rename')} | |||
</ActionsDropdownItem> | |||
) : ( | |||
<ActionsDropdownItem | |||
className="js-delete" | |||
destructive={true} | |||
onClick={this.handleDeleteClick}> | |||
{translate( | |||
isPullRequest(branchLike) ? 'branches.pull_request.delete' : 'branches.delete' | |||
)} | |||
</ActionsDropdownItem> | |||
)} | |||
</ActionsDropdown> | |||
{this.state.deleting && ( | |||
<DeleteBranchModal | |||
branchLike={branchLike} | |||
component={component} | |||
onClose={this.handleDeletingStop} | |||
onDelete={this.handleChange} | |||
/> | |||
)} | |||
{this.state.renaming && isMainBranch(branchLike) && ( | |||
<RenameBranchModal | |||
branch={branchLike} | |||
component={component} | |||
onClose={this.handleRenamingStop} | |||
onRename={this.handleChange} | |||
/> | |||
)} | |||
</td> | |||
); | |||
} | |||
render() { | |||
const { branchLike, component, isOrphan } = this.props; | |||
const indented = (isShortLivingBranch(branchLike) || isPullRequest(branchLike)) && !isOrphan; | |||
return ( | |||
<tr> | |||
<td> | |||
<BranchIcon | |||
branchLike={branchLike} | |||
className={classNames('little-spacer-right', { 'big-spacer-left': indented })} | |||
/> | |||
{getBranchLikeDisplayName(branchLike)} | |||
{isMainBranch(branchLike) && ( | |||
<div className="badge spacer-left">{translate('branches.main_branch')}</div> | |||
)} | |||
</td> | |||
<td className="thin nowrap"> | |||
<BranchStatus branchLike={branchLike} component={component} /> | |||
</td> | |||
<td className="thin nowrap text-right big-spacer-left"> | |||
{branchLike.analysisDate && <DateFromNow date={branchLike.analysisDate} />} | |||
</td> | |||
{this.renderActions()} | |||
</tr> | |||
); | |||
} | |||
} |
@@ -26,7 +26,7 @@ import { getBranchLikeDisplayName, isPullRequest } from '../../../helpers/branch | |||
interface Props { | |||
branchLike: T.BranchLike; | |||
component: string; | |||
component: T.Component; | |||
onClose: () => void; | |||
onDelete: () => void; | |||
} | |||
@@ -52,12 +52,12 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State> | |||
this.setState({ loading: true }); | |||
const request = isPullRequest(this.props.branchLike) | |||
? deletePullRequest({ | |||
project: this.props.component, | |||
project: this.props.component.key, | |||
pullRequest: this.props.branchLike.key | |||
}) | |||
: deleteBranch({ | |||
branch: this.props.branchLike.name, | |||
project: this.props.component | |||
project: this.props.component.key | |||
}); | |||
request.then( | |||
() => { | |||
@@ -77,7 +77,9 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State> | |||
render() { | |||
const { branchLike } = this.props; | |||
const header = translate( | |||
isPullRequest(branchLike) ? 'branches.pull_request.delete' : 'branches.delete' | |||
isPullRequest(branchLike) | |||
? 'project_branch_pull_request.pull_request.delete' | |||
: 'project_branch_pull_request.branch.delete' | |||
); | |||
return ( | |||
@@ -89,8 +91,8 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State> | |||
<div className="modal-body"> | |||
{translateWithParameters( | |||
isPullRequest(branchLike) | |||
? 'branches.pull_request.delete.are_you_sure' | |||
: 'branches.delete.are_you_sure', | |||
? 'project_branch_pull_request.pull_request.delete.are_you_sure' | |||
: 'project_branch_pull_request.branch.delete.are_you_sure', | |||
getBranchLikeDisplayName(branchLike) | |||
)} | |||
</div> |
@@ -0,0 +1,88 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { connect } from 'react-redux'; | |||
import { getValues } from '../../../api/settings'; | |||
import { getAppState, Store } from '../../../store/rootReducer'; | |||
import LifetimeInformationRenderer from './LifetimeInformationRenderer'; | |||
interface Props { | |||
canAdmin?: boolean; | |||
} | |||
interface State { | |||
branchAndPullRequestLifeTimeInDays?: string; | |||
loading: boolean; | |||
} | |||
export const BRANCH_PULL_REQUEST_LIFETIME_SETTING = | |||
'sonar.dbcleaner.daysBeforeDeletingInactiveShortLivingBranches'; | |||
export class LifetimeInformation extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { loading: true }; | |||
componentDidMount() { | |||
this.mounted = true; | |||
this.fetchBranchAndPullRequestLifetimeSetting(); | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
fetchBranchAndPullRequestLifetimeSetting() { | |||
getValues({ keys: BRANCH_PULL_REQUEST_LIFETIME_SETTING }).then( | |||
settings => { | |||
if (this.mounted) { | |||
this.setState({ | |||
loading: false, | |||
branchAndPullRequestLifeTimeInDays: settings.length > 0 ? settings[0].value : undefined | |||
}); | |||
} | |||
}, | |||
() => { | |||
if (this.mounted) { | |||
this.setState({ loading: false }); | |||
} | |||
} | |||
); | |||
} | |||
render() { | |||
const { canAdmin } = this.props; | |||
const { branchAndPullRequestLifeTimeInDays, loading } = this.state; | |||
return ( | |||
<LifetimeInformationRenderer | |||
branchAndPullRequestLifeTimeInDays={branchAndPullRequestLifeTimeInDays} | |||
canAdmin={canAdmin} | |||
loading={loading} | |||
/> | |||
); | |||
} | |||
} | |||
const mapStoreToProps = (state: Store) => ({ | |||
canAdmin: getAppState(state).canAdmin | |||
}); | |||
export default connect(mapStoreToProps)(LifetimeInformation); |
@@ -0,0 +1,61 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Link } from 'react-router'; | |||
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { formatMeasure } from 'sonar-ui-common/helpers/measures'; | |||
export interface LifetimeInformationRendererProps { | |||
branchAndPullRequestLifeTimeInDays?: string; | |||
canAdmin?: boolean; | |||
loading: boolean; | |||
} | |||
export function LifetimeInformationRenderer(props: LifetimeInformationRendererProps) { | |||
const { branchAndPullRequestLifeTimeInDays, canAdmin, loading } = props; | |||
return ( | |||
<DeferredSpinner loading={loading}> | |||
{branchAndPullRequestLifeTimeInDays && ( | |||
<p className="page-description"> | |||
<FormattedMessage | |||
defaultMessage={translate('project_branch_pull_request.lifetime_information')} | |||
id="project_branch_pull_request.lifetime_information" | |||
values={{ days: formatMeasure(branchAndPullRequestLifeTimeInDays, 'INT') }} | |||
/> | |||
{canAdmin && ( | |||
<FormattedMessage | |||
defaultMessage={translate('project_branch_pull_request.lifetime_information.admin')} | |||
id="project_branch_pull_request.lifetime_information.admin" | |||
values={{ | |||
settings: <Link to="/admin/settings">{translate('settings.page')}</Link> | |||
}} | |||
/> | |||
)} | |||
</p> | |||
)} | |||
</DeferredSpinner> | |||
); | |||
} | |||
export default React.memo(LifetimeInformationRenderer); |
@@ -25,7 +25,7 @@ import { renameBranch } from '../../../api/branches'; | |||
interface Props { | |||
branch: T.MainBranch; | |||
component: string; | |||
component: T.Component; | |||
onClose: () => void; | |||
onRename: () => void; | |||
} | |||
@@ -53,7 +53,7 @@ export default class RenameBranchModal extends React.PureComponent<Props, State> | |||
return; | |||
} | |||
this.setState({ loading: true }); | |||
renameBranch(this.props.component, this.state.name).then( | |||
renameBranch(this.props.component.key, this.state.name).then( | |||
() => { | |||
if (this.mounted) { | |||
this.setState({ loading: false }); | |||
@@ -74,7 +74,7 @@ export default class RenameBranchModal extends React.PureComponent<Props, State> | |||
render() { | |||
const { branch } = this.props; | |||
const header = translate('branches.rename'); | |||
const header = translate('project_branch_pull_request.branch.rename'); | |||
const submitDisabled = | |||
this.state.loading || !this.state.name || this.state.name === branch.name; | |||
@@ -17,48 +17,38 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { mount, shallow } from 'enzyme'; | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { getValues } from '../../../../api/settings'; | |||
import { | |||
mockLongLivingBranch, | |||
mockMainBranch, | |||
mockPullRequest, | |||
mockShortLivingBranch | |||
} from '../../../../helpers/testMocks'; | |||
import App from '../App'; | |||
import { mockSetOfBranchAndPullRequest } from '../../../../helpers/mocks/branch-pull-request'; | |||
import { mockComponent } from '../../../../helpers/testMocks'; | |||
import { App, AppProps } from '../App'; | |||
import BranchLikeTabs from '../BranchLikeTabs'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should properly notify that a branch or a pr has been changed/deleted', () => { | |||
const onBranchesChange = jest.fn(); | |||
const wrapper = shallowRender({ onBranchesChange }); | |||
jest.mock('../../../../api/settings', () => ({ | |||
getValues: jest.fn(() => Promise.resolve([])) | |||
})); | |||
wrapper | |||
.find(BranchLikeTabs) | |||
.props() | |||
.onBranchesChange(); | |||
beforeEach(() => { | |||
jest.clearAllMocks(); | |||
expect(onBranchesChange).toHaveBeenCalled(); | |||
}); | |||
it('renders sorted list of branches', () => { | |||
const branchLikes = [ | |||
mockMainBranch(), | |||
mockLongLivingBranch(), | |||
mockShortLivingBranch(), | |||
mockPullRequest(), | |||
mockShortLivingBranch({ mergeBranch: 'foobar', name: 'feature', isOrphan: true }) | |||
]; | |||
const wrapper = shallow( | |||
function shallowRender(props?: Partial<AppProps>) { | |||
return shallow( | |||
<App | |||
branchLikes={branchLikes} | |||
canAdmin={true} | |||
component={{ key: 'foo' }} | |||
branchLikes={mockSetOfBranchAndPullRequest()} | |||
component={mockComponent()} | |||
onBranchesChange={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
wrapper.setState({ branchLifeTime: '100', loading: false }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('fetches branch life time setting on mount', () => { | |||
mount(<App branchLikes={[]} component={{ key: 'foo' }} onBranchesChange={jest.fn()} />); | |||
expect(getValues).toBeCalledWith({ | |||
keys: 'sonar.dbcleaner.daysBeforeDeletingInactiveShortLivingBranches' | |||
}); | |||
}); | |||
} |
@@ -0,0 +1,62 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { | |||
mockComponent, | |||
mockLongLivingBranch, | |||
mockMainBranch, | |||
mockPullRequest, | |||
mockShortLivingBranch | |||
} from '../../../../helpers/testMocks'; | |||
import { BranchLikeRowRenderer, BranchLikeRowRendererProps } from '../BranchLikeRowRenderer'; | |||
it('should render correctly for pull request', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render correctly for short lived branch', () => { | |||
const wrapper = shallowRender({ branchLike: mockShortLivingBranch() }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render correctly for long lived branch', () => { | |||
const wrapper = shallowRender({ branchLike: mockLongLivingBranch() }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render correctly for mai branch', () => { | |||
const wrapper = shallowRender({ branchLike: mockMainBranch() }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props?: Partial<BranchLikeRowRendererProps>) { | |||
return shallow( | |||
<BranchLikeRowRenderer | |||
branchLike={mockPullRequest()} | |||
component={mockComponent()} | |||
onDelete={jest.fn()} | |||
onRename={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockSetOfBranchAndPullRequest } from '../../../../helpers/mocks/branch-pull-request'; | |||
import { mockComponent } from '../../../../helpers/testMocks'; | |||
import { BranchLikeRowRenderer } from '../BranchLikeRowRenderer'; | |||
import { BranchLikeTableRenderer, BranchLikeTableRendererProps } from '../BranchLikeTableRenderer'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should properly propagate delete event', () => { | |||
const onDelete = jest.fn(); | |||
const wrapper = shallowRender({ onDelete }); | |||
wrapper | |||
.find(BranchLikeRowRenderer) | |||
.first() | |||
.props() | |||
.onDelete(); | |||
expect(onDelete).toHaveBeenCalled(); | |||
}); | |||
it('should properly propagate rename event', () => { | |||
const onDelete = jest.fn(); | |||
const onRename = jest.fn(); | |||
const wrapper = shallowRender({ onDelete, onRename }); | |||
wrapper | |||
.find(BranchLikeRowRenderer) | |||
.first() | |||
.props() | |||
.onRename(); | |||
expect(onRename).toHaveBeenCalled(); | |||
}); | |||
function shallowRender(props?: Partial<BranchLikeTableRendererProps>) { | |||
return shallow( | |||
<BranchLikeTableRenderer | |||
branchLikes={mockSetOfBranchAndPullRequest()} | |||
component={mockComponent()} | |||
onDelete={jest.fn()} | |||
onRename={jest.fn()} | |||
tableTitle="tableTitle" | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,126 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; | |||
import { mockSetOfBranchAndPullRequest } from '../../../../helpers/mocks/branch-pull-request'; | |||
import { mockComponent, mockMainBranch, mockPullRequest } from '../../../../helpers/testMocks'; | |||
import { BranchLikeTableRenderer } from '../BranchLikeTableRenderer'; | |||
import BranchLikeTabs, { Tabs } from '../BranchLikeTabs'; | |||
import DeleteBranchModal from '../DeleteBranchModal'; | |||
import RenameBranchModal from '../RenameBranchModal'; | |||
it('should render all tabs correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper.state().currentTab).toBe(Tabs.Branch); | |||
expect(wrapper).toMatchSnapshot(); | |||
const onSelect = wrapper.find(BoxedTabs).prop('onSelect') as ((currentTab: Tabs) => void); | |||
onSelect(Tabs.PullRequest); | |||
expect(wrapper.state().currentTab).toBe(Tabs.PullRequest); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render deletion modal correctly', () => { | |||
const onBranchesChange = jest.fn(); | |||
const wrapper = shallowRender({ onBranchesChange }); | |||
wrapper | |||
.find(BranchLikeTableRenderer) | |||
.props() | |||
.onDelete(mockPullRequest()); | |||
expect(wrapper.state().deleting).toBeDefined(); | |||
expect(wrapper.find(DeleteBranchModal)).toMatchSnapshot(); | |||
wrapper | |||
.find(DeleteBranchModal) | |||
.props() | |||
.onClose(); | |||
expect(wrapper.state().deleting).toBeUndefined(); | |||
expect(wrapper.find(DeleteBranchModal).exists()).toBeFalsy(); | |||
wrapper | |||
.find(BranchLikeTableRenderer) | |||
.props() | |||
.onDelete(mockPullRequest()); | |||
wrapper | |||
.find(DeleteBranchModal) | |||
.props() | |||
.onDelete(); | |||
expect(wrapper.state().deleting).toBeUndefined(); | |||
expect(wrapper.find(DeleteBranchModal).exists()).toBeFalsy(); | |||
expect(onBranchesChange).toHaveBeenCalled(); | |||
}); | |||
it('should render renaming modal correctly', () => { | |||
const onBranchesChange = jest.fn(); | |||
const wrapper = shallowRender({ onBranchesChange }); | |||
wrapper | |||
.find(BranchLikeTableRenderer) | |||
.props() | |||
.onRename(mockMainBranch()); | |||
expect(wrapper.state().renaming).toBeDefined(); | |||
expect(wrapper.find(RenameBranchModal)).toMatchSnapshot(); | |||
wrapper | |||
.find(RenameBranchModal) | |||
.props() | |||
.onClose(); | |||
expect(wrapper.state().renaming).toBeUndefined(); | |||
expect(wrapper.find(RenameBranchModal).exists()).toBeFalsy(); | |||
wrapper | |||
.find(BranchLikeTableRenderer) | |||
.props() | |||
.onRename(mockMainBranch()); | |||
wrapper | |||
.find(RenameBranchModal) | |||
.props() | |||
.onRename(); | |||
expect(wrapper.state().renaming).toBeUndefined(); | |||
expect(wrapper.find(RenameBranchModal).exists()).toBeFalsy(); | |||
expect(onBranchesChange).toHaveBeenCalled(); | |||
}); | |||
it('should NOT render renaming modal for non-main branch', () => { | |||
const wrapper = shallowRender(); | |||
wrapper | |||
.find(BranchLikeTableRenderer) | |||
.props() | |||
.onRename(mockPullRequest()); | |||
expect(wrapper.state().renaming).toBeDefined(); | |||
expect(wrapper.find(RenameBranchModal).exists()).toBeFalsy(); | |||
}); | |||
function shallowRender(props: Partial<BranchLikeTabs['props']> = {}) { | |||
return shallow<BranchLikeTabs>( | |||
<BranchLikeTabs | |||
branchLikes={mockSetOfBranchAndPullRequest()} | |||
component={mockComponent()} | |||
onBranchesChange={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,77 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { click } from 'sonar-ui-common/helpers/testUtils'; | |||
import { mockPullRequest, mockShortLivingBranch } from '../../../../helpers/testMocks'; | |||
import BranchRow from '../BranchRow'; | |||
const mainBranch: T.MainBranch = { isMain: true, name: 'master' }; | |||
const shortBranch = mockShortLivingBranch(); | |||
const pullRequest = mockPullRequest(); | |||
it('renders main branch', () => { | |||
expect(shallowRender(mainBranch)).toMatchSnapshot(); | |||
}); | |||
it('renders short-living branch', () => { | |||
expect(shallowRender(shortBranch)).toMatchSnapshot(); | |||
}); | |||
it('renders pull request', () => { | |||
expect(shallowRender(pullRequest)).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(); | |||
}); | |||
it('deletes pull request', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender(pullRequest, onChange); | |||
click(wrapper.find('.js-delete')); | |||
(wrapper.find('DeleteBranchModal').prop('onDelete') as Function)(); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
function shallowRender(branchLike: T.BranchLike, onChange: () => void = jest.fn()) { | |||
const wrapper = shallow( | |||
<BranchRow branchLike={branchLike} component="foo" isOrphan={false} onChange={onChange} /> | |||
); | |||
(wrapper.instance() as any).mounted = true; | |||
return wrapper; | |||
} |
@@ -17,11 +17,16 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow, ShallowWrapper } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { click, doAsync, submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; | |||
import { deleteBranch, deletePullRequest } from '../../../../api/branches'; | |||
import { mockPullRequest, mockShortLivingBranch } from '../../../../helpers/testMocks'; | |||
import { | |||
mockComponent, | |||
mockPullRequest, | |||
mockShortLivingBranch | |||
} from '../../../../helpers/testMocks'; | |||
import DeleteBranchModal from '../DeleteBranchModal'; | |||
jest.mock('../../../../api/branches', () => ({ | |||
@@ -101,7 +106,7 @@ function shallowRender( | |||
const wrapper = shallow<DeleteBranchModal>( | |||
<DeleteBranchModal | |||
branchLike={branchLike} | |||
component="foo" | |||
component={mockComponent({ key: 'foo' })} | |||
onClose={onClose} | |||
onDelete={onDelete} | |||
/> |
@@ -0,0 +1,43 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; | |||
import { getValues } from '../../../../api/settings'; | |||
import { BRANCH_PULL_REQUEST_LIFETIME_SETTING, LifetimeInformation } from '../LifetimeInformation'; | |||
jest.mock('../../../../api/settings', () => ({ | |||
getValues: jest.fn().mockResolvedValue([{ value: '45' }]) | |||
})); | |||
it('should render correctly', async () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot('initial_state'); | |||
await waitAndUpdate(wrapper); | |||
expect(getValues).toHaveBeenCalledWith({ keys: BRANCH_PULL_REQUEST_LIFETIME_SETTING }); | |||
expect(wrapper).toMatchSnapshot('after_fetching_data'); | |||
}); | |||
function shallowRender(props: Partial<LifetimeInformation['props']> = {}) { | |||
return shallow<LifetimeInformation>(<LifetimeInformation canAdmin={true} {...props} />); | |||
} |
@@ -0,0 +1,51 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { | |||
LifetimeInformationRenderer, | |||
LifetimeInformationRendererProps | |||
} from '../LifetimeInformationRenderer'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render correctly when user is admin', () => { | |||
const wrapper = shallowRender({ canAdmin: true }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render correctly if not lifetime has been fetch', () => { | |||
const wrapper = shallowRender({ branchAndPullRequestLifeTimeInDays: undefined }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props?: Partial<LifetimeInformationRendererProps>) { | |||
return shallow( | |||
<LifetimeInformationRenderer | |||
branchAndPullRequestLifeTimeInDays="30" | |||
loading={true} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -17,15 +17,16 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
/* eslint-disable import/first */ | |||
jest.mock('../../../../api/branches', () => ({ renameBranch: jest.fn() })); | |||
import { shallow, ShallowWrapper } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { change, click, doAsync, submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; | |||
import { renameBranch } from '../../../../api/branches'; | |||
import { mockComponent } from '../../../../helpers/testMocks'; | |||
import RenameBranchModal from '../RenameBranchModal'; | |||
jest.mock('../../../../api/branches', () => ({ renameBranch: jest.fn() })); | |||
beforeEach(() => { | |||
(renameBranch as jest.Mock<any>).mockClear(); | |||
}); | |||
@@ -78,7 +79,12 @@ it('stops loading on WS error', async () => { | |||
function shallowRender(onRename: () => void = jest.fn(), onClose: () => void = jest.fn()) { | |||
const branch: T.MainBranch = { isMain: true, name: 'master' }; | |||
const wrapper = shallow<RenameBranchModal>( | |||
<RenameBranchModal branch={branch} component="foo" onClose={onClose} onRename={onRename} /> | |||
<RenameBranchModal | |||
branch={branch} | |||
component={mockComponent({ key: 'foo' })} | |||
onClose={onClose} | |||
onRename={onRename} | |||
/> | |||
); | |||
wrapper.instance().mounted = true; | |||
return wrapper; |
@@ -1,165 +1,108 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`renders sorted list of branches 1`] = ` | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="page page-limited" | |||
> | |||
<header | |||
className="page-header" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
project_branches.page | |||
<h1> | |||
project_branch_pull_request.page | |||
</h1> | |||
<p | |||
className="page-description" | |||
> | |||
project_branches.page.description | |||
</p> | |||
<p | |||
className="page-description" | |||
> | |||
<FormattedMessage | |||
defaultMessage="project_branches.page.life_time" | |||
id="project_branches.page.life_time" | |||
values={ | |||
Object { | |||
"days": "100", | |||
} | |||
} | |||
/> | |||
<br /> | |||
<FormattedMessage | |||
defaultMessage="project_branches.page.life_time.admin" | |||
id="project_branches.page.life_time.admin" | |||
values={ | |||
Object { | |||
"settings": <Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to="/admin/settings" | |||
> | |||
settings.page | |||
</Link>, | |||
} | |||
} | |||
/> | |||
</p> | |||
<Connect(LifetimeInformation) /> | |||
</header> | |||
<div | |||
className="boxed-group boxed-group-inner" | |||
> | |||
<table | |||
className="data zebra zebra-hover" | |||
> | |||
<thead> | |||
<tr> | |||
<th> | |||
branch | |||
</th> | |||
<th | |||
className="thin nowrap" | |||
> | |||
status | |||
</th> | |||
<th | |||
className="thin nowrap text-right big-spacer-left" | |||
> | |||
branches.last_analysis_date | |||
</th> | |||
<th | |||
className="thin nowrap text-right" | |||
> | |||
actions | |||
</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<BranchRow | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": true, | |||
"name": "master", | |||
} | |||
} | |||
component="foo" | |||
isOrphan={false} | |||
onChange={[MockFunction]} | |||
/> | |||
<BranchRow | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "1001", | |||
"target": "master", | |||
"title": "Foo Bar feature", | |||
} | |||
} | |||
component="foo" | |||
onChange={[MockFunction]} | |||
/> | |||
<BranchRow | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"mergeBranch": "master", | |||
"name": "feature/foo", | |||
"type": "SHORT", | |||
} | |||
} | |||
component="foo" | |||
onChange={[MockFunction]} | |||
/> | |||
<BranchRow | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "branch-6.7", | |||
"type": "LONG", | |||
} | |||
} | |||
component="foo" | |||
isOrphan={false} | |||
onChange={[MockFunction]} | |||
/> | |||
<tr> | |||
<td | |||
colSpan={4} | |||
> | |||
<div | |||
className="display-inline-block text-middle" | |||
> | |||
branches.orphan_branches | |||
</div> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay="branches.orphan_branches.tooltip" | |||
/> | |||
</td> | |||
</tr> | |||
<BranchRow | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"isOrphan": true, | |||
"mergeBranch": "foobar", | |||
"name": "feature", | |||
"type": "SHORT", | |||
} | |||
} | |||
component="foo" | |||
isOrphan={true} | |||
onChange={[MockFunction]} | |||
/> | |||
</tbody> | |||
</table> | |||
</div> | |||
<BranchLikeTabs | |||
branchLikes={ | |||
Array [ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"mergeBranch": "master", | |||
"name": "slb-1", | |||
"type": "SHORT", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-1", | |||
"type": "LONG", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": true, | |||
"name": "master", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "1", | |||
"target": "master", | |||
"title": "PR-1", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"mergeBranch": "llb-1", | |||
"name": "slb-2", | |||
"type": "SHORT", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "2", | |||
"target": "master", | |||
"title": "PR-2", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-3", | |||
"type": "LONG", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-2", | |||
"type": "LONG", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"isOrphan": true, | |||
"key": "2", | |||
"target": "llb-100", | |||
"title": "PR-2", | |||
}, | |||
] | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
onBranchesChange={[MockFunction]} | |||
/> | |||
</div> | |||
`; |
@@ -1,11 +1,68 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`renders main branch 1`] = ` | |||
exports[`should render correctly for long lived branch 1`] = ` | |||
<tr> | |||
<td> | |||
<BranchIcon | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "branch-6.7", | |||
"type": "LONG", | |||
} | |||
} | |||
className="little-spacer-right" | |||
/> | |||
branch-6.7 | |||
</td> | |||
<td | |||
className="thin nowrap" | |||
> | |||
<Connect(BranchStatus) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "branch-6.7", | |||
"type": "LONG", | |||
} | |||
} | |||
component="my-project" | |||
/> | |||
</td> | |||
<td | |||
className="thin nowrap text-right big-spacer-left" | |||
> | |||
<DateFromNow | |||
date="2018-01-01" | |||
/> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ActionsDropdown | |||
className="big-spacer-left" | |||
> | |||
<ActionsDropdownItem | |||
className="js-delete" | |||
destructive={true} | |||
onClick={[MockFunction]} | |||
> | |||
project_branch_pull_request.branch.delete | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly for mai branch 1`] = ` | |||
<tr> | |||
<td> | |||
<BranchIcon | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": true, | |||
"name": "master", | |||
} | |||
@@ -25,34 +82,39 @@ exports[`renders main branch 1`] = ` | |||
<Connect(BranchStatus) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": true, | |||
"name": "master", | |||
} | |||
} | |||
component="foo" | |||
component="my-project" | |||
/> | |||
</td> | |||
<td | |||
className="thin nowrap text-right big-spacer-left" | |||
/> | |||
> | |||
<DateFromNow | |||
date="2018-01-01" | |||
/> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ActionsDropdown | |||
className="ig-spacer-left" | |||
className="big-spacer-left" | |||
> | |||
<ActionsDropdownItem | |||
className="js-rename" | |||
onClick={[Function]} | |||
onClick={[MockFunction]} | |||
> | |||
branches.rename | |||
project_branch_pull_request.branch.rename | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`renders pull request 1`] = ` | |||
exports[`should render correctly for pull request 1`] = ` | |||
<tr> | |||
<td> | |||
<BranchIcon | |||
@@ -66,7 +128,7 @@ exports[`renders pull request 1`] = ` | |||
"title": "Foo Bar feature", | |||
} | |||
} | |||
className="little-spacer-right big-spacer-left" | |||
className="little-spacer-right" | |||
/> | |||
1001 – Foo Bar feature | |||
</td> | |||
@@ -84,7 +146,7 @@ exports[`renders pull request 1`] = ` | |||
"title": "Foo Bar feature", | |||
} | |||
} | |||
component="foo" | |||
component="my-project" | |||
/> | |||
</td> | |||
<td | |||
@@ -98,21 +160,21 @@ exports[`renders pull request 1`] = ` | |||
className="thin nowrap text-right" | |||
> | |||
<ActionsDropdown | |||
className="ig-spacer-left" | |||
className="big-spacer-left" | |||
> | |||
<ActionsDropdownItem | |||
className="js-delete" | |||
destructive={true} | |||
onClick={[Function]} | |||
onClick={[MockFunction]} | |||
> | |||
branches.pull_request.delete | |||
project_branch_pull_request.pull_request.delete | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`renders short-living branch 1`] = ` | |||
exports[`should render correctly for short lived branch 1`] = ` | |||
<tr> | |||
<td> | |||
<BranchIcon | |||
@@ -125,7 +187,7 @@ exports[`renders short-living branch 1`] = ` | |||
"type": "SHORT", | |||
} | |||
} | |||
className="little-spacer-right big-spacer-left" | |||
className="little-spacer-right" | |||
/> | |||
feature/foo | |||
</td> | |||
@@ -142,7 +204,7 @@ exports[`renders short-living branch 1`] = ` | |||
"type": "SHORT", | |||
} | |||
} | |||
component="foo" | |||
component="my-project" | |||
/> | |||
</td> | |||
<td | |||
@@ -156,14 +218,14 @@ exports[`renders short-living branch 1`] = ` | |||
className="thin nowrap text-right" | |||
> | |||
<ActionsDropdown | |||
className="ig-spacer-left" | |||
className="big-spacer-left" | |||
> | |||
<ActionsDropdownItem | |||
className="js-delete" | |||
destructive={true} | |||
onClick={[Function]} | |||
onClick={[MockFunction]} | |||
> | |||
branches.delete | |||
project_branch_pull_request.branch.delete | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</td> |
@@ -0,0 +1,368 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="boxed-group boxed-group-inner" | |||
> | |||
<table | |||
className="data zebra zebra-hover" | |||
> | |||
<thead> | |||
<tr> | |||
<th> | |||
tableTitle | |||
</th> | |||
<th | |||
className="thin nowrap" | |||
> | |||
status | |||
</th> | |||
<th | |||
className="thin nowrap text-right big-spacer-left" | |||
> | |||
branches.last_analysis_date | |||
</th> | |||
<th | |||
className="thin nowrap text-right" | |||
> | |||
actions | |||
</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"mergeBranch": "master", | |||
"name": "slb-1", | |||
"type": "SHORT", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="branch-slb-1" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-1", | |||
"type": "LONG", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="branch-llb-1" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": true, | |||
"name": "master", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="branch-master" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "1", | |||
"target": "master", | |||
"title": "PR-1", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="pull-request-1" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"mergeBranch": "llb-1", | |||
"name": "slb-2", | |||
"type": "SHORT", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="branch-slb-2" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "2", | |||
"target": "master", | |||
"title": "PR-2", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="pull-request-2" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-3", | |||
"type": "LONG", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="branch-llb-3" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-2", | |||
"type": "LONG", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="branch-llb-2" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
<Memo(BranchLikeRowRenderer) | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"isOrphan": true, | |||
"key": "2", | |||
"target": "llb-100", | |||
"title": "PR-2", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
key="pull-request-2" | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
/> | |||
</tbody> | |||
</table> | |||
</div> | |||
`; |
@@ -0,0 +1,274 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render all tabs correctly 1`] = ` | |||
<Fragment> | |||
<BoxedTabs | |||
onSelect={[Function]} | |||
selected={0} | |||
tabs={ | |||
Array [ | |||
Object { | |||
"key": 0, | |||
"label": <React.Fragment> | |||
<ShortLivingBranchIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
project_branch_pull_request.tabs.branches | |||
</span> | |||
</React.Fragment>, | |||
}, | |||
Object { | |||
"key": 1, | |||
"label": <React.Fragment> | |||
<PullRequestIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
project_branch_pull_request.tabs.pull_requests | |||
</span> | |||
</React.Fragment>, | |||
}, | |||
] | |||
} | |||
/> | |||
<Memo(BranchLikeTableRenderer) | |||
branchLikes={ | |||
Array [ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": true, | |||
"name": "master", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-1", | |||
"type": "LONG", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-2", | |||
"type": "LONG", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"name": "llb-3", | |||
"type": "LONG", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"mergeBranch": "master", | |||
"name": "slb-1", | |||
"type": "SHORT", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": false, | |||
"mergeBranch": "llb-1", | |||
"name": "slb-2", | |||
"type": "SHORT", | |||
}, | |||
] | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
tableTitle="project_branch_pull_request.table.branch" | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`should render all tabs correctly 2`] = ` | |||
<Fragment> | |||
<BoxedTabs | |||
onSelect={[Function]} | |||
selected={1} | |||
tabs={ | |||
Array [ | |||
Object { | |||
"key": 0, | |||
"label": <React.Fragment> | |||
<ShortLivingBranchIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
project_branch_pull_request.tabs.branches | |||
</span> | |||
</React.Fragment>, | |||
}, | |||
Object { | |||
"key": 1, | |||
"label": <React.Fragment> | |||
<PullRequestIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
project_branch_pull_request.tabs.pull_requests | |||
</span> | |||
</React.Fragment>, | |||
}, | |||
] | |||
} | |||
/> | |||
<Memo(BranchLikeTableRenderer) | |||
branchLikes={ | |||
Array [ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "1", | |||
"target": "master", | |||
"title": "PR-1", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "2", | |||
"target": "master", | |||
"title": "PR-2", | |||
}, | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"isOrphan": true, | |||
"key": "2", | |||
"target": "llb-100", | |||
"title": "PR-2", | |||
}, | |||
] | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
onDelete={[Function]} | |||
onRename={[Function]} | |||
tableTitle="project_branch_pull_request.table.pull_request" | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`should render deletion modal correctly 1`] = ` | |||
<DeleteBranchModal | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"base": "master", | |||
"branch": "feature/foo/bar", | |||
"key": "1001", | |||
"target": "master", | |||
"title": "Foo Bar feature", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
onClose={[Function]} | |||
onDelete={[Function]} | |||
/> | |||
`; | |||
exports[`should render renaming modal correctly 1`] = ` | |||
<RenameBranchModal | |||
branch={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
"isMain": true, | |||
"name": "master", | |||
} | |||
} | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
onClose={[Function]} | |||
onRename={[Function]} | |||
/> | |||
`; |
@@ -2,14 +2,14 @@ | |||
exports[`renders 1`] = ` | |||
<Modal | |||
contentLabel="branches.delete" | |||
contentLabel="project_branch_pull_request.branch.delete" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
branches.delete | |||
project_branch_pull_request.branch.delete | |||
</h2> | |||
</header> | |||
<form | |||
@@ -18,7 +18,7 @@ exports[`renders 1`] = ` | |||
<div | |||
className="modal-body" | |||
> | |||
branches.delete.are_you_sure.feature/foo | |||
project_branch_pull_request.branch.delete.are_you_sure.feature/foo | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
@@ -41,14 +41,14 @@ exports[`renders 1`] = ` | |||
exports[`renders 2`] = ` | |||
<Modal | |||
contentLabel="branches.delete" | |||
contentLabel="project_branch_pull_request.branch.delete" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
branches.delete | |||
project_branch_pull_request.branch.delete | |||
</h2> | |||
</header> | |||
<form | |||
@@ -57,7 +57,7 @@ exports[`renders 2`] = ` | |||
<div | |||
className="modal-body" | |||
> | |||
branches.delete.are_you_sure.feature/foo | |||
project_branch_pull_request.branch.delete.are_you_sure.feature/foo | |||
</div> | |||
<footer | |||
className="modal-foot" |
@@ -0,0 +1,16 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: after_fetching_data 1`] = ` | |||
<Memo(LifetimeInformationRenderer) | |||
branchAndPullRequestLifeTimeInDays="45" | |||
canAdmin={true} | |||
loading={false} | |||
/> | |||
`; | |||
exports[`should render correctly: initial_state 1`] = ` | |||
<Memo(LifetimeInformationRenderer) | |||
canAdmin={true} | |||
loading={true} | |||
/> | |||
`; |
@@ -0,0 +1,65 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<DeferredSpinner | |||
loading={true} | |||
timeout={100} | |||
> | |||
<p | |||
className="page-description" | |||
> | |||
<FormattedMessage | |||
defaultMessage="project_branch_pull_request.lifetime_information" | |||
id="project_branch_pull_request.lifetime_information" | |||
values={ | |||
Object { | |||
"days": "30", | |||
} | |||
} | |||
/> | |||
</p> | |||
</DeferredSpinner> | |||
`; | |||
exports[`should render correctly if not lifetime has been fetch 1`] = ` | |||
<DeferredSpinner | |||
loading={true} | |||
timeout={100} | |||
/> | |||
`; | |||
exports[`should render correctly when user is admin 1`] = ` | |||
<DeferredSpinner | |||
loading={true} | |||
timeout={100} | |||
> | |||
<p | |||
className="page-description" | |||
> | |||
<FormattedMessage | |||
defaultMessage="project_branch_pull_request.lifetime_information" | |||
id="project_branch_pull_request.lifetime_information" | |||
values={ | |||
Object { | |||
"days": "30", | |||
} | |||
} | |||
/> | |||
<FormattedMessage | |||
defaultMessage="project_branch_pull_request.lifetime_information.admin" | |||
id="project_branch_pull_request.lifetime_information.admin" | |||
values={ | |||
Object { | |||
"settings": <Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to="/admin/settings" | |||
> | |||
settings.page | |||
</Link>, | |||
} | |||
} | |||
/> | |||
</p> | |||
</DeferredSpinner> | |||
`; |
@@ -2,7 +2,7 @@ | |||
exports[`renders 1`] = ` | |||
<Modal | |||
contentLabel="branches.rename" | |||
contentLabel="project_branch_pull_request.branch.rename" | |||
onRequestClose={[MockFunction]} | |||
size="small" | |||
> | |||
@@ -10,7 +10,7 @@ exports[`renders 1`] = ` | |||
className="modal-head" | |||
> | |||
<h2> | |||
branches.rename | |||
project_branch_pull_request.branch.rename | |||
</h2> | |||
</header> | |||
<form | |||
@@ -65,7 +65,7 @@ exports[`renders 1`] = ` | |||
exports[`renders 2`] = ` | |||
<Modal | |||
contentLabel="branches.rename" | |||
contentLabel="project_branch_pull_request.branch.rename" | |||
onRequestClose={[MockFunction]} | |||
size="small" | |||
> | |||
@@ -73,7 +73,7 @@ exports[`renders 2`] = ` | |||
className="modal-head" | |||
> | |||
<h2> | |||
branches.rename | |||
project_branch_pull_request.branch.rename | |||
</h2> | |||
</header> | |||
<form | |||
@@ -128,7 +128,7 @@ exports[`renders 2`] = ` | |||
exports[`renders 3`] = ` | |||
<Modal | |||
contentLabel="branches.rename" | |||
contentLabel="project_branch_pull_request.branch.rename" | |||
onRequestClose={[MockFunction]} | |||
size="small" | |||
> | |||
@@ -136,7 +136,7 @@ exports[`renders 3`] = ` | |||
className="modal-head" | |||
> | |||
<h2> | |||
branches.rename | |||
project_branch_pull_request.branch.rename | |||
</h2> | |||
</header> | |||
<form |
@@ -21,7 +21,7 @@ import { lazyLoad } from 'sonar-ui-common/components/lazyLoad'; | |||
const routes = [ | |||
{ | |||
indexRoute: { component: lazyLoad(() => import('./components/AppContainer')) } | |||
indexRoute: { component: lazyLoad(() => import('./components/App')) } | |||
} | |||
]; | |||
@@ -17,12 +17,24 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { connect } from 'react-redux'; | |||
import { getAppState, Store } from '../../../store/rootReducer'; | |||
import App from './App'; | |||
const mapStateToProps = (state: Store) => ({ | |||
canAdmin: getAppState(state).canAdmin | |||
}); | |||
import { | |||
mockLongLivingBranch, | |||
mockMainBranch, | |||
mockPullRequest, | |||
mockShortLivingBranch | |||
} from '../testMocks'; | |||
export default connect(mapStateToProps)(App); | |||
export function mockSetOfBranchAndPullRequest(): T.BranchLike[] { | |||
return [ | |||
mockShortLivingBranch({ name: 'slb-1' }), | |||
mockLongLivingBranch({ name: 'llb-1' }), | |||
mockMainBranch(), | |||
mockPullRequest({ key: '1', title: 'PR-1' }), | |||
mockShortLivingBranch({ name: 'slb-2', mergeBranch: 'llb-1' }), | |||
mockPullRequest({ key: '2', title: 'PR-2' }), | |||
mockLongLivingBranch({ name: 'llb-3' }), | |||
mockLongLivingBranch({ name: 'llb-2' }), | |||
mockPullRequest({ key: '2', title: 'PR-2', target: 'llb-100', isOrphan: true }) | |||
]; | |||
} |
@@ -534,10 +534,18 @@ portfolio_deletion.page.description=This portfolio and its sub-portfolios will b | |||
application_deletion.page.description=Delete this application. Application projects will not be deleted. Projects referenced by this application will not be deleted. This operation cannot be undone. | |||
application.branches.help=Easily create Application branches composed of the branches of projects in your application. | |||
application.branches.link=Create Branch | |||
project_branches.page=Branches & Pull Requests | |||
project_branches.page.description=Use this page to manage project branches and pull requests. | |||
project_branches.page.life_time=Short-lived branches and pull requests are permanently deleted after {days} days without analysis. | |||
project_branches.page.life_time.admin=You can adjust this value globally in {settings}. | |||
project_branch_pull_request.page=Branches & Pull Requests | |||
project_branch_pull_request.lifetime_information=Branches and Pull Requests are permanently deleted after {days} days without analysis. | |||
project_branch_pull_request.lifetime_information.admin=You can adjust this value globally in {settings}. | |||
project_branch_pull_request.branch.rename=Rename branch | |||
project_branch_pull_request.branch.delete=Delete branch | |||
project_branch_pull_request.branch.delete.are_you_sure=Are you sure you want to delete branch "{0}"? | |||
project_branch_pull_request.pull_request.delete=Delete Pull Request | |||
project_branch_pull_request.pull_request.delete.are_you_sure=Are you sure you want to delete Pull Request "{0}"? | |||
project_branch_pull_request.tabs.branches=Branches | |||
project_branch_pull_request.tabs.pull_requests=Pull Requests | |||
project_branch_pull_request.table.branch=Branch | |||
project_branch_pull_request.table.pull_request=Pull Request | |||
project_baseline.page=New Code Period | |||
project_baseline.page.description=Use this page to manage the New Code Period of your project. {link} | |||
project_baseline.page.description.link=Learn More | |||
@@ -3180,11 +3188,6 @@ onboarding.tutorial.return_to_tutorial=Return to tutorial | |||
# BRANCHES | |||
# | |||
#------------------------------------------------------------------------------ | |||
branches.delete=Delete Branch | |||
branches.delete.are_you_sure=Are you sure you want to delete branch "{0}"? | |||
branches.pull_request.delete=Delete Pull Request | |||
branches.pull_request.delete.are_you_sure=Are you sure you want to delete pull request "{0}"? | |||
branches.rename=Rename Branch | |||
branches.manage=Manage branches | |||
branches.orphan_branch=Orphan Branch | |||
branches.orphan_branches=Orphan Branches & Pull Requests |