Pārlūkot izejas kodu

apply branches feedback (#2535)

tags/6.6-RC1
Stas Vilchik pirms 6 gadiem
vecāks
revīzija
94f500e009
46 mainītis faili ar 738 papildinājumiem un 1147 dzēšanām
  1. 16
    16
      server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
  2. 0
    17
      server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx
  3. 10
    0
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css
  4. 4
    5
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
  5. 16
    33
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx
  6. 23
    19
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenu.tsx
  7. 104
    27
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx
  8. 18
    33
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
  9. 3
    3
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
  10. 4
    3
      server/sonar-web/src/main/js/app/components/nav/component/DeleteBranchModal.tsx
  11. 0
    48
      server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx
  12. 4
    3
      server/sonar-web/src/main/js/app/components/nav/component/RenameBranchModal.tsx
  13. 4
    2
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx
  14. 15
    19
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx
  15. 6
    3
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx
  16. 48
    50
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx
  17. 35
    49
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx
  18. 3
    12
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx
  19. 4
    4
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/DeleteBranchModal-test.tsx
  20. 0
    26
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/NoBranchSupportPopup-test.tsx
  21. 4
    4
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/RenameBranchModal-test.tsx
  22. 0
    2
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
  23. 0
    41
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap
  24. 7
    0
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap
  25. 9
    3
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap
  26. 1
    53
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap
  27. 0
    0
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap
  28. 0
    37
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap
  29. 0
    0
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
  30. 1
    1
      server/sonar-web/src/main/js/app/types.ts
  31. 0
    2
      server/sonar-web/src/main/js/app/utils/startReactApp.js
  32. 7
    3
      server/sonar-web/src/main/js/apps/overview/components/App.js
  33. 0
    60
      server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx
  34. 0
    139
      server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx
  35. 0
    34
      server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx
  36. 0
    65
      server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx
  37. 0
    73
      server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
  38. 0
    100
      server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap
  39. 0
    30
      server/sonar-web/src/main/js/apps/projectBranches/routes.ts
  40. 31
    11
      server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
  41. 17
    6
      server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx
  42. 296
    51
      server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
  43. 1
    1
      server/sonar-web/src/main/js/apps/settings/components/App.js
  44. 26
    26
      server/sonar-web/src/main/js/components/common/BranchStatus.tsx
  45. 18
    30
      server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap
  46. 3
    3
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 16
- 16
server/sonar-web/src/main/js/app/components/ComponentContainer.tsx Parādīt failu

@@ -122,36 +122,36 @@ export default class ComponentContainer extends React.PureComponent<Props, State
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>
);
}

+ 0
- 17
server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx Parādīt failu

@@ -106,20 +106,3 @@ it("doesn't load branches portfolio", () => {
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');
});

+ 10
- 0
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css Parādīt failu

@@ -25,4 +25,14 @@
.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;
}

+ 4
- 5
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx Parādīt failu

@@ -24,7 +24,7 @@ import ComponentNavMeta from './ComponentNavMeta';
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';
@@ -34,8 +34,8 @@ interface Props {
branches: Branch[];
currentBranch?: Branch;
component: Component;
conf: ComponentConfiguration;
location: {};
onBranchesChange: () => void;
}

interface State {
@@ -102,17 +102,17 @@ export default class ComponentNav extends React.PureComponent<Props, 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}
@@ -122,7 +122,6 @@ export default class ComponentNav extends React.PureComponent<Props, State> {
<ComponentNavMenu
branch={this.props.currentBranch}
component={this.props.component}
conf={this.props.conf}
// to re-render selected menu item
location={this.props.location}
/>

+ 16
- 33
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx Parādīt failu

@@ -22,7 +22,6 @@ import * as classNames from 'classnames';
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';
@@ -33,9 +32,10 @@ import Tooltip from '../../../../components/controls/Tooltip';

interface Props {
branches: Branch[];
component: Component;
currentBranch: Branch;
location?: any;
project: Component;
onBranchesChange: () => void;
}

interface State {
@@ -53,8 +53,7 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
};

static contextTypes = {
branchesEnabled: PropTypes.bool.isRequired,
onSonarCloud: PropTypes.bool
branchesEnabled: PropTypes.bool.isRequired
};

componentDidMount() {
@@ -63,8 +62,8 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State

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 });
@@ -75,11 +74,16 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
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 = () => {
@@ -117,12 +121,15 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
};

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;
};
@@ -160,35 +167,11 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
</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) {

+ 23
- 19
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenu.tsx Parādīt failu

@@ -28,14 +28,15 @@ import {
} 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 {
@@ -65,7 +66,9 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
);

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();
}
};
@@ -125,11 +128,23 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
};

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();

@@ -179,8 +194,10 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
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}
/>
@@ -191,23 +208,10 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
};

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>
);
}

+ 104
- 27
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx Parādīt failu

@@ -20,48 +20,125 @@
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>
);
}
}

+ 18
- 33
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx Parādīt failu

@@ -21,7 +21,7 @@ import * as React from 'react';
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,
@@ -48,7 +48,6 @@ const SETTINGS_URLS = [
interface Props {
branch?: Branch;
component: Component;
conf: ComponentConfiguration;
location?: any;
}

@@ -74,6 +73,10 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
return this.props.component.qualifier === 'APP';
}

getConfiguration() {
return this.props.component.configuration || {};
}

renderDashboardLink() {
if (this.props.branch && isShortLivingBranch(this.props.branch)) {
return null;
@@ -193,7 +196,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
renderAdministration() {
const { branch } = this.props;

if (!this.props.conf.showSettings || (branch && isShortLivingBranch(branch))) {
if (!this.getConfiguration().showSettings || (branch && isShortLivingBranch(branch))) {
return null;
}

@@ -209,7 +212,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
pathname: '/project/settings',
query: { branch: getBranchName(branch), id: this.props.component.key }
}}>
{translate('layout.settings')}&nbsp;
{translate('branches.branch_settings')}
</Link>
</li>
);
@@ -238,7 +241,6 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
renderAdministrationLinks() {
return [
this.renderSettingsLink(),
this.renderBranchesLink(),
this.renderProfilesLink(),
this.renderQualityGateLink(),
this.renderCustomMeasuresLink(),
@@ -252,7 +254,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}

renderSettingsLink() {
if (!this.props.conf.showSettings || this.isApplication() || this.isPortfolio()) {
if (!this.getConfiguration().showSettings || this.isApplication() || this.isPortfolio()) {
return null;
}
return (
@@ -272,23 +274,8 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
);
}

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 (
@@ -303,7 +290,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}

renderQualityGateLink() {
if (!this.props.conf.showQualityGates) {
if (!this.getConfiguration().showQualityGates) {
return null;
}
return (
@@ -318,7 +305,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}

renderCustomMeasuresLink() {
if (!this.props.conf.showManualMeasures) {
if (!this.getConfiguration().showManualMeasures) {
return null;
}
return (
@@ -333,7 +320,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}

renderLinksLink() {
if (!this.props.conf.showLinks) {
if (!this.getConfiguration().showLinks) {
return null;
}
return (
@@ -348,7 +335,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}

renderPermissionsLink() {
if (!this.props.conf.showPermissions) {
if (!this.getConfiguration().showPermissions) {
return null;
}
return (
@@ -363,7 +350,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}

renderBackgroundTasksLink() {
if (!this.props.conf.showBackgroundTasks) {
if (!this.getConfiguration().showBackgroundTasks) {
return null;
}
return (
@@ -378,7 +365,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}

renderUpdateKeyLink() {
if (!this.props.conf.showUpdateKey) {
if (!this.getConfiguration().showUpdateKey) {
return null;
}
return (
@@ -395,7 +382,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
renderDeletionLink() {
const { qualifier } = this.props.component;

if (!this.props.conf.showSettings) {
if (!this.getConfiguration().showSettings) {
return null;
}

@@ -426,7 +413,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
};

renderAdminExtensions() {
const extensions = this.props.conf.extensions || [];
const extensions = this.getConfiguration().extensions || [];
return extensions.map(e => this.renderExtension(e, true));
}

@@ -446,9 +433,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
{translate('more')}&nbsp;
<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>
);
}

+ 3
- 3
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx Parādīt failu

@@ -20,7 +20,7 @@
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';
@@ -30,7 +30,6 @@ import { isShortLivingBranch } from '../../../../helpers/branches';
interface Props {
branch?: Branch;
component: Component;
conf: ComponentConfiguration;
incremental?: boolean;
isInProgress?: boolean;
isFailed?: boolean;
@@ -39,7 +38,8 @@ interface Props {

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)}`;

server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx → server/sonar-web/src/main/js/app/components/nav/component/DeleteBranchModal.tsx Parādīt failu

@@ -19,9 +19,9 @@
*/
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';
import { deleteBranch } from '../../../../api/branches';
import { Branch } from '../../../../app/types';
import { translate, translateWithParameters } from '../../../../helpers/l10n';

interface Props {
branch: Branch;
@@ -66,6 +66,7 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State>

handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
event.stopPropagation();
this.props.onClose();
};


+ 0
- 48
server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx Parādīt failu

@@ -1,48 +0,0 @@
/*
* 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>
);
}

server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx → server/sonar-web/src/main/js/app/components/nav/component/RenameBranchModal.tsx Parādīt failu

@@ -19,9 +19,9 @@
*/
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';
import { renameBranch } from '../../../../api/branches';
import { Branch } from '../../../../app/types';
import { translate } from '../../../../helpers/l10n';

interface Props {
branch: Branch;
@@ -70,6 +70,7 @@ export default class RenameBranchModal extends React.PureComponent<Props, State>

handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
event.stopPropagation();
this.props.onClose();
};


+ 4
- 2
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx Parādīt failu

@@ -62,13 +62,15 @@ const component = {

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,

+ 15
- 19
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx Parādīt failu

@@ -38,8 +38,9 @@ it('renders main branch', () => {
shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
component={component}
currentBranch={branch}
project={component}
onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
)
@@ -59,8 +60,9 @@ it('renders short-living branch', () => {
shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
component={component}
currentBranch={branch}
project={component}
onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
)
@@ -73,8 +75,9 @@ it('opens menu', () => {
const wrapper = shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
component={component}
currentBranch={branch}
project={component}
onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
);
@@ -87,7 +90,12 @@ it('renders single branch popup', () => {
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();
@@ -96,29 +104,17 @@ it('renders single branch popup', () => {
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();
});

+ 6
- 3
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx Parādīt failu

@@ -36,9 +36,10 @@ it('renders list', () => {
shallow(
<ComponentNavBranchesMenu
branches={[mainBranch(), shortBranch('foo'), longBranch('bar'), shortBranch('baz', true)]}
component={project}
currentBranch={mainBranch()}
onBranchesChange={jest.fn()}
onClose={jest.fn()}
project={project}
/>
)
).toMatchSnapshot();
@@ -48,9 +49,10 @@ it('searches', () => {
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' });
@@ -61,9 +63,10 @@ it('selects next & previous', () => {
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);

+ 48
- 50
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx Parādīt failu

@@ -19,63 +19,61 @@
*/
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}
/>
);
}

+ 35
- 49
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx Parādīt failu

@@ -20,55 +20,47 @@
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();
});

@@ -79,10 +71,9 @@ it('should work for short-living branches', () => {
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();
@@ -90,12 +81,15 @@ it('should work for short-living branches', () => {

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()
);
});
@@ -103,20 +97,12 @@ it('should work for long-living branches', () => {
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();
}
});

+ 3
- 12
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx Parādīt failu

@@ -39,12 +39,7 @@ it('renders incremental badge', () => {
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);
}
@@ -58,9 +53,7 @@ it('renders status of short-living branch', () => {
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', () => {
@@ -70,7 +63,5 @@ 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();
});

server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx → server/sonar-web/src/main/js/app/components/nav/component/__tests__/DeleteBranchModal-test.tsx Parādīt failu

@@ -17,14 +17,14 @@
* 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() }));
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';
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();

+ 0
- 26
server/sonar-web/src/main/js/app/components/nav/component/__tests__/NoBranchSupportPopup-test.tsx Parādīt failu

@@ -1,26 +0,0 @@
/*
* 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();
});

server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/RenameBranchModal-test.tsx → server/sonar-web/src/main/js/app/components/nav/component/__tests__/RenameBranchModal-test.tsx Parādīt failu

@@ -17,14 +17,14 @@
* 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() }));
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';
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();

+ 0
- 2
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap Parādīt failu

@@ -50,7 +50,6 @@ exports[`renders 1`] = `
"qualifier": "TRK",
}
}
conf={Object {}}
incremental={true}
isFailed={true}
isInProgress={true}
@@ -72,7 +71,6 @@ exports[`renders 1`] = `
"qualifier": "TRK",
}
}
conf={Object {}}
location={Object {}}
/>
</ContextNavBar>

+ 0
- 41
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap Parādīt failu

@@ -26,47 +26,6 @@ exports[`renders main branch 1`] = `
</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"

+ 7
- 0
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap Parādīt failu

@@ -39,6 +39,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
onBranchesChange={[Function]}
onSelect={[Function]}
selected={true}
/>
@@ -78,6 +79,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -101,6 +103,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -120,6 +123,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -159,6 +163,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -213,6 +218,7 @@ exports[`searches 1`] = `
"key": "component",
}
}
onBranchesChange={[Function]}
onSelect={[Function]}
selected={true}
/>
@@ -232,6 +238,7 @@ exports[`searches 1`] = `
"key": "component",
}
}
onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>

+ 9
- 3
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap Parādīt failu

@@ -17,7 +17,9 @@ exports[`renders main branch 1`] = `
}
}
>
<div>
<div
className="navbar-context-meta-branch-menu-item-name"
>
<BranchIcon
branch={
Object {
@@ -70,7 +72,9 @@ exports[`renders short-living branch 1`] = `
}
}
>
<div>
<div
className="navbar-context-meta-branch-menu-item-name"
>
<BranchIcon
branch={
Object {
@@ -132,7 +136,9 @@ exports[`renders short-living orhpan branch 1`] = `
}
}
>
<div>
<div
className="navbar-context-meta-branch-menu-item-name"
>
<BranchIcon
branch={
Object {

+ 1
- 53
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap Parādīt failu

@@ -129,23 +129,6 @@ exports[`should work for all qualifiers 1`] = `
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"
@@ -755,8 +738,7 @@ exports[`should work for long-living branches 1`] = `
}
}
>
layout.settings
 
branches.branch_settings
</Link>
</li>
</NavBarTabs>
@@ -1029,23 +1011,6 @@ exports[`should work with extensions 1`] = `
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"
@@ -1251,23 +1216,6 @@ exports[`should work with multiple extensions 1`] = `
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"

server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap → server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap Parādīt failu


+ 0
- 37
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap Parādīt failu

@@ -1,37 +0,0 @@
// 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>
`;

server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap → server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap Parādīt failu


+ 1
- 1
server/sonar-web/src/main/js/app/types.ts Parādīt failu

@@ -79,7 +79,7 @@ export interface Component {
version?: string;
}

export interface ComponentConfiguration {
interface ComponentConfiguration {
extensions?: ComponentExtension[];
showBackgroundTasks?: boolean;
showLinks?: boolean;

+ 0
- 2
server/sonar-web/src/main/js/app/utils/startReactApp.js Parādīt failu

@@ -55,7 +55,6 @@ import permissionTemplatesRoutes from '../../apps/permission-templates/routes';
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';
@@ -207,7 +206,6 @@ const startReactApp = () => {
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>

+ 7
- 3
server/sonar-web/src/main/js/apps/overview/components/App.js Parādīt failu

@@ -56,19 +56,23 @@ export default class App extends React.PureComponent {
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;
}


+ 0
- 60
server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx Parādīt failu

@@ -1,60 +0,0 @@
/*
* 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>
);
}

+ 0
- 139
server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx Parādīt failu

@@ -1,139 +0,0 @@
/*
* 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>
);
}
}

+ 0
- 34
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx Parādīt failu

@@ -1,34 +0,0 @@
/*
* 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();
});

+ 0
- 65
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx Parādīt failu

@@ -1,65 +0,0 @@
/*
* 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;
}

+ 0
- 73
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap Parādīt failu

@@ -1,73 +0,0 @@
// 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>
`;

+ 0
- 100
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap Parādīt failu

@@ -1,100 +0,0 @@
// 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>
`;

+ 0
- 30
server/sonar-web/src/main/js/apps/projectBranches/routes.ts Parādīt failu

@@ -1,30 +0,0 @@
/*
* 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;

+ 31
- 11
server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx Parādīt failu

@@ -34,6 +34,7 @@ interface Props {
}

interface State {
advanced: boolean;
branch: string;
createdProject?: { key: string; name: string };
key: string;
@@ -50,6 +51,7 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
constructor(props: Props) {
super(props);
this.state = {
advanced: false,
branch: '',
key: '',
loading: false,
@@ -71,6 +73,12 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
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 });
@@ -162,17 +170,6 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
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')}
@@ -202,6 +199,29 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
</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">

+ 17
- 6
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx Parādīt failu

@@ -26,7 +26,7 @@ jest.mock('../../../api/components', () => ({
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>;

@@ -46,9 +46,6 @@ it('creates project', async () => {
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' }
});
@@ -58,7 +55,7 @@ it('creates project', async () => {

submit(wrapper.find('form'));
expect(createProject).toBeCalledWith({
branch: 'branch',
branch: '',
name: 'name',
organization: 'org',
project: 'key',
@@ -66,7 +63,21 @@ it('creates project', async () => {
});
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();
});

+ 296
- 51
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap Parādīt failu

@@ -52,23 +52,6 @@ exports[`creates project 1`] = `
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"
>
@@ -111,6 +94,17 @@ exports[`creates project 1`] = `
/>
</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"
@@ -186,23 +180,6 @@ exports[`creates project 2`] = `
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"
>
@@ -245,6 +222,17 @@ exports[`creates project 2`] = `
/>
</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"
@@ -320,23 +308,6 @@ exports[`creates project 3`] = `
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"
>
@@ -379,6 +350,17 @@ exports[`creates project 3`] = `
/>
</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"
@@ -467,3 +449,266 @@ exports[`creates project 4`] = `
</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>
`;

+ 1
- 1
server/sonar-web/src/main/js/apps/settings/components/App.js Parādīt failu

@@ -98,7 +98,7 @@ export default class App extends React.PureComponent {
link: (
<Link
to={{
pathname: '/project/branches',
pathname: '/project/settings',
query: { id: this.props.component && this.props.component.key }
}}>
{translate('branches.settings_hint_tab')}

+ 26
- 26
server/sonar-web/src/main/js/components/common/BranchStatus.tsx Parādīt failu

@@ -41,35 +41,35 @@ export default function BranchStatus({ branch, concise = false }: Props) {
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 {

+ 18
- 30
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap Parādīt failu

@@ -20,28 +20,24 @@ exports[`renders status of short-living branches 1`] = `
<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>
`;
@@ -50,28 +46,24 @@ exports[`renders status of short-living branches 2`] = `
<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>
`;
@@ -80,28 +72,24 @@ exports[`renders status of short-living branches 3`] = `
<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>
`;

+ 3
- 3
sonar-core/src/main/resources/org/sonar/l10n/core.properties Parādīt failu

@@ -3178,13 +3178,13 @@ branches.no_support.header.text=Analyze each branch of your project separately w
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


#------------------------------------------------------------------------------

Notiek ielāde…
Atcelt
Saglabāt