import { ComponentContext } from './ComponentContext';
import ComponentContainerNotFound from './ComponentContainerNotFound';
import ComponentNav from './nav/component/ComponentNav';
-import handleRequiredAuthorization from '../utils/handleRequiredAuthorization';
import { getBranches, getPullRequests } from '../../api/branches';
import { getTasksForComponent, getAnalysisStatus } from '../../api/ce';
import { getComponentData } from '../../api/components';
import { getMeasures } from '../../api/measures';
import { getComponentNavigation } from '../../api/nav';
-import { fetchOrganization } from '../../store/rootActions';
+import { fetchOrganization, requireAuthorization } from '../../store/rootActions';
import { STATUSES } from '../../apps/background-tasks/constants';
import {
isPullRequest,
isShortLivingBranch,
getBranchLikeQuery
} from '../../helpers/branches';
-import { Store, getAppState } from '../../store/rootReducer';
+import { isSonarCloud } from '../../helpers/system';
+import { withRouter, Router, Location } from '../../components/hoc/withRouter';
interface Props {
- appState: Pick<T.AppState, 'organizationsEnabled'>;
- children: any;
+ children: React.ReactElement<any>;
fetchOrganization: (organization: string) => void;
- location: {
- query: { branch?: string; id: string; pullRequest?: string };
- };
+ location: Pick<Location, 'query'>;
+ requireAuthorization: (router: Pick<Router, 'replace'>) => void;
+ router: Pick<Router, 'replace'>;
}
interface State {
this.fetchComponent();
}
- componentWillReceiveProps(nextProps: Props) {
+ componentDidUpdate(prevProps: Props) {
if (
- nextProps.location.query.id !== this.props.location.query.id ||
- nextProps.location.query.branch !== this.props.location.query.branch ||
- nextProps.location.query.pullRequest !== this.props.location.query.pullRequest
+ prevProps.location.query.id !== this.props.location.query.id ||
+ prevProps.location.query.branch !== this.props.location.query.branch ||
+ prevProps.location.query.pullRequest !== this.props.location.query.pullRequest
) {
- this.fetchComponent(nextProps);
+ this.fetchComponent();
}
}
qualifier: component.breadcrumbs[component.breadcrumbs.length - 1].qualifier
});
- fetchComponent(props = this.props) {
- const { branch, id: key, pullRequest } = props.location.query;
+ fetchComponent() {
+ const { branch, id: key, pullRequest } = this.props.location.query;
this.setState({ loading: true });
const onError = (response?: Response) => {
if (this.mounted) {
if (response && response.status === 403) {
- handleRequiredAuthorization();
+ this.props.requireAuthorization(this.props.router);
} else {
- this.setState({ loading: false });
+ this.setState({ component: undefined, loading: false });
}
}
};
.then(([nav, data]) => {
const component = this.addQualifier({ ...nav, ...data });
- if (this.props.appState.organizationsEnabled) {
+ if (isSonarCloud()) {
this.props.fetchOrganization(component.organization);
}
return component;
}
}
-const mapStateToProps = (state: Store) => ({
- appState: getAppState(state)
-});
-
-const mapDispatchToProps = { fetchOrganization };
+const mapDispatchToProps = { fetchOrganization, requireAuthorization };
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(ComponentContainer);
+export default withRouter(
+ connect(
+ null,
+ mapDispatchToProps
+ )(ComponentContainer)
+);
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { shallow, mount } from 'enzyme';
+import { shallow } from 'enzyme';
+import { Location } from 'history';
import { ComponentContainer } from '../ComponentContainer';
import { getBranches, getPullRequests } from '../../../api/branches';
import { getTasksForComponent } from '../../../api/ce';
import { STATUSES } from '../../../apps/background-tasks/constants';
import { waitAndUpdate } from '../../../helpers/testUtils';
import { getMeasures } from '../../../api/measures';
+import { isSonarCloud } from '../../../helpers/system';
jest.mock('../../../api/branches', () => ({
getBranches: jest.fn().mockResolvedValue([]),
})
}));
+jest.mock('../../../helpers/system', () => ({
+ isSonarCloud: jest.fn()
+}));
+
// mock this, because some of its children are using redux store
jest.mock('../nav/component/ComponentNav', () => ({
default: () => null
const Inner = () => <div />;
+const mainBranch: T.MainBranch = { isMain: true, name: 'master' };
+
beforeEach(() => {
(getBranches as jest.Mock).mockClear();
(getPullRequests as jest.Mock).mockClear();
(getComponentNavigation as jest.Mock).mockClear();
(getTasksForComponent as jest.Mock).mockClear();
(getMeasures as jest.Mock).mockClear();
+ (isSonarCloud as jest.Mock).mockReturnValue(false).mockClear();
});
it('changes component', () => {
- const wrapper = shallow<ComponentContainer>(
- <ComponentContainer
- appState={{ organizationsEnabled: false }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'foo' } }}>
- <Inner />
- </ComponentContainer>
- );
- wrapper.instance().mounted = true;
+ const wrapper = shallowRender();
wrapper.setState({
branchLikes: [{ isMain: true, name: 'master' }],
component: { qualifier: 'TRK', visibility: 'public' } as T.Component,
expect(wrapper.state().component).toEqual({ qualifier: 'TRK', visibility: 'private' });
});
-it("loads branches for module's project", async () => {
- (getComponentNavigation as jest.Mock<any>).mockResolvedValueOnce({
- breadcrumbs: [
- { key: 'projectKey', name: 'project', qualifier: 'TRK' },
- { key: 'moduleKey', name: 'module', qualifier: 'BRC' }
- ]
- });
-
- mount(
- <ComponentContainer
- appState={{ organizationsEnabled: false }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'moduleKey' } }}>
- <Inner />
- </ComponentContainer>
- );
-
- await new Promise(setImmediate);
- expect(getBranches).toBeCalledWith('projectKey');
- expect(getPullRequests).toBeCalledWith('projectKey');
- expect(getComponentData).toBeCalledWith({ component: 'moduleKey', branch: undefined });
- expect(getComponentNavigation).toBeCalledWith({ component: 'moduleKey', branch: undefined });
-});
-
it("doesn't load branches portfolio", async () => {
- const wrapper = mount(
- <ComponentContainer
- appState={{ organizationsEnabled: false }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'portfolioKey' } }}>
- <Inner />
- </ComponentContainer>
- );
-
+ const wrapper = shallowRender({ location: { query: { id: 'portfolioKey' } } as Location });
await new Promise(setImmediate);
expect(getBranches).not.toBeCalled();
expect(getPullRequests).not.toBeCalled();
});
it('updates branches on change', () => {
- const wrapper = shallow(
- <ComponentContainer
- appState={{ organizationsEnabled: false }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'portfolioKey' } }}>
- <Inner />
- </ComponentContainer>
- );
- (wrapper.instance() as ComponentContainer).mounted = true;
+ const wrapper = shallowRender({ location: { query: { id: 'portfolioKey' } } as Location });
wrapper.setState({
- branches: [{ isMain: true }],
- component: { breadcrumbs: [{ key: 'projectKey', name: 'project', qualifier: 'TRK' }] },
+ branchLikes: [mainBranch],
+ component: {
+ breadcrumbs: [{ key: 'projectKey', name: 'project', qualifier: 'TRK' }]
+ } as T.Component,
loading: false
});
- (wrapper.find(Inner).prop('onBranchesChange') as Function)();
+ wrapper.find(Inner).prop<Function>('onBranchesChange')();
expect(getBranches).toBeCalledWith('projectKey');
expect(getPullRequests).toBeCalledWith('projectKey');
});
{ isMain: false, mergeBranch: 'master', name: 'feature', type: 'SHORT' }
]);
(getPullRequests as jest.Mock<any>).mockResolvedValueOnce([]);
- const wrapper = shallow(
- <ComponentContainer
- appState={{ organizationsEnabled: false }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'foo', branch: 'feature' } }}>
- <Inner />
- </ComponentContainer>
- );
- (wrapper.instance() as ComponentContainer).mounted = true;
+ const wrapper = shallowRender({
+ location: { query: { id: 'foo', branch: 'feature' } } as Location
+ });
wrapper.setState({
- branches: [{ isMain: true }],
- component: { breadcrumbs: [{ key: 'foo', name: 'Foo', qualifier: 'TRK' }] },
+ branchLikes: [mainBranch],
+ component: { breadcrumbs: [{ key: 'foo', name: 'Foo', qualifier: 'TRK' }] } as T.Component,
loading: false
});
});
it('loads organization', async () => {
+ (isSonarCloud as jest.Mock).mockReturnValue(true);
(getComponentData as jest.Mock<any>).mockResolvedValueOnce({ organization: 'org' });
const fetchOrganization = jest.fn();
- mount(
- <ComponentContainer
- appState={{ organizationsEnabled: true }}
- fetchOrganization={fetchOrganization}
- location={{ query: { id: 'foo' } }}>
- <Inner />
- </ComponentContainer>
- );
-
+ shallowRender({ fetchOrganization });
await new Promise(setImmediate);
expect(fetchOrganization).toBeCalledWith('org');
});
it('fetches status', async () => {
(getComponentData as jest.Mock<any>).mockResolvedValueOnce({ organization: 'org' });
- mount(
- <ComponentContainer
- appState={{ organizationsEnabled: true }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'foo' } }}>
- <Inner />
- </ComponentContainer>
- );
-
+ shallowRender();
await new Promise(setImmediate);
expect(getTasksForComponent).toBeCalledWith('portfolioKey');
});
it('filters correctly the pending tasks for a main branch', () => {
- const wrapper = shallow(
- <ComponentContainer
- appState={{ organizationsEnabled: false }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'foo' } }}>
- <Inner />
- </ComponentContainer>
- );
-
- const component = wrapper.instance() as ComponentContainer;
+ const wrapper = shallowRender();
+ const component = wrapper.instance();
const mainBranch: T.MainBranch = { isMain: true, name: 'master' };
const shortBranch: T.ShortLivingBranch = {
isMain: false,
jest.useFakeTimers();
const inProgressTask = { id: 'foo', status: STATUSES.IN_PROGRESS } as T.Task;
(getTasksForComponent as jest.Mock<any>).mockResolvedValueOnce({ queue: [inProgressTask] });
- const wrapper = shallow(
- <ComponentContainer
- appState={{ organizationsEnabled: false }}
- fetchOrganization={jest.fn()}
- location={{ query: { id: 'foo' } }}>
- <Inner />
- </ComponentContainer>
- );
+ const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(getComponentNavigation).toHaveBeenCalledTimes(1);
expect(getTasksForComponent).toHaveBeenCalledTimes(1);
expect(getComponentNavigation).toHaveBeenCalledTimes(2);
expect(getTasksForComponent).toHaveBeenCalledTimes(3);
});
+
+function shallowRender(props: Partial<ComponentContainer['props']> = {}) {
+ return shallow<ComponentContainer>(
+ <ComponentContainer
+ fetchOrganization={jest.fn()}
+ location={{ query: { id: 'foo' } }}
+ requireAuthorization={jest.fn()}
+ router={{ replace: jest.fn() }}
+ {...props}>
+ <Inner />
+ </ComponentContainer>
+ );
+}
import OrganizationHelmet from '../../../../components/common/OrganizationHelmet';
import OrganizationLink from '../../../../components/ui/OrganizationLink';
import { sanitizeAlmId } from '../../../../helpers/almIntegrations';
-import { collapsePath } from '../../../../helpers/path';
import { getProjectUrl, getBaseUrl } from '../../../../helpers/urls';
import { isSonarCloud } from '../../../../helpers/system';
import { isMainBranch } from '../../../../helpers/branches';
function renderBreadcrumbs(breadcrumbs: T.Breadcrumb[], shouldLinkLast: boolean) {
const lastItem = breadcrumbs[breadcrumbs.length - 1];
return breadcrumbs.map((item, index) => {
- const isPath = item.qualifier === 'DIR';
- const itemName = isPath ? collapsePath(item.name, 15) : item.name;
-
return (
<React.Fragment key={item.key}>
{index === 0 && <QualifierIcon className="spacer-right" qualifier={lastItem.qualifier} />}
className="navbar-context-header-breadcrumb-link link-base-color link-no-underline"
title={item.name}
to={getProjectUrl(item.key)}>
- {itemName}
+ {item.name}
</Link>
) : (
<span className="navbar-context-header-breadcrumb-link" title={item.name}>
- {itemName}
+ {item.name}
</span>
)}
{index < breadcrumbs.length - 1 && <span className="slash-separator" />}
}
renderSecurityReports() {
- const { branchLike, component } = this.props;
-
- if (component.qualifier === 'BRC' || component.qualifier === 'DIR') {
- return null;
- }
+ const { branchLike } = this.props;
if (isShortLivingBranch(branchLike) || isPullRequest(branchLike)) {
return null;
});
it('should work for all qualifiers', () => {
- ['TRK', 'BRC', 'VW', 'SVW', 'APP'].forEach(checkWithQualifier);
- expect.assertions(5);
+ ['TRK', 'VW', 'SVW', 'APP'].forEach(checkWithQualifier);
+ expect.assertions(4);
function checkWithQualifier(qualifier: string) {
const component = { ...baseComponent, configuration: { showSettings: true }, qualifier };
`;
exports[`should work for all qualifiers 2`] = `
-<NavBarTabs>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- overview.page
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- className=""
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "id": "foo",
- "resolved": "false",
- },
- }
- }
- >
- issues.page
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/component_measures",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- layout.measures
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/code",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- code.page
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_activity.page
- </Link>
- </li>
- <Dropdown
- data-test="administration"
- overlay={
- <ul
- className="menu"
- >
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/settings",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_settings.page
- </Link>
- </li>
- </ul>
- }
- tagName="li"
- >
- <Component />
- </Dropdown>
-</NavBarTabs>
-`;
-
-exports[`should work for all qualifiers 3`] = `
<NavBarTabs>
<li>
<Link
</NavBarTabs>
`;
-exports[`should work for all qualifiers 4`] = `
+exports[`should work for all qualifiers 3`] = `
<NavBarTabs>
<li>
<Link
</NavBarTabs>
`;
-exports[`should work for all qualifiers 5`] = `
+exports[`should work for all qualifiers 4`] = `
<NavBarTabs>
<li>
<Link
import { getSuggestions } from '../../../api/components';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { scrollToElement } from '../../../helpers/scrolling';
-import { getProjectUrl } from '../../../helpers/urls';
+import { getProjectUrl, getCodeUrl } from '../../../helpers/urls';
import './Search.css';
const SearchResults = lazyLoad(() => import('./SearchResults'));
return next;
}, []);
- mergeWithRecentlyBrowsed = (components: ComponentResult[]) => {
- const recentlyBrowsed = RecentHistory.get().map(component => ({
- ...component,
- isRecentlyBrowsed: true,
- qualifier: component.icon.toUpperCase()
- }));
- return uniqBy([...components, ...recentlyBrowsed], 'key');
+ findFile = (key: string) => {
+ const findInResults = (results: ComponentResult[] | undefined) =>
+ results && results.find(r => r.key === key);
+
+ const file = findInResults(this.state.results['FIL']);
+ if (file) {
+ return file;
+ }
+
+ const test = findInResults(this.state.results['UTS']);
+ if (test) {
+ return test;
+ }
+
+ return undefined;
};
stopLoading = () => {
openSelected = () => {
const { selected } = this.state;
+
if (selected) {
if (selected.startsWith('qualifier###')) {
this.searchMore(selected.substr(12));
} else {
- this.props.router.push(getProjectUrl(selected));
+ const file = this.findFile(selected);
+ if (file) {
+ this.props.router.push(getCodeUrl(file.project!, undefined, file.key));
+ } else {
+ this.props.router.push(getProjectUrl(selected));
+ }
this.closeSearch();
}
}
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import ClockIcon from '../../../components/icons-components/ClockIcon';
import Tooltip from '../../../components/controls/Tooltip';
-import { getProjectUrl } from '../../../helpers/urls';
+import { getProjectUrl, getCodeUrl } from '../../../helpers/urls';
interface Props {
appState: Pick<T.AppState, 'organizationsEnabled'>;
render() {
const { component } = this.props;
+ const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS';
+ const to = isFile
+ ? getCodeUrl(component.project!, undefined, component.key)
+ : getProjectUrl(component.key);
+
return (
<li
className={this.props.selected ? 'active' : undefined}
overlay={component.key}
placement="left"
visible={this.state.tooltipVisible}>
- <Link
- data-key={component.key}
- onClick={this.props.onClose}
- to={getProjectUrl(component.key)}>
+ <Link data-key={component.key} onClick={this.props.onClose} to={to}>
<span className="navbar-search-item-link" onMouseEnter={this.handleMouseEnter}>
<span className="navbar-search-item-icons little-spacer-right">
{component.isFavorite && <FavoriteIcon favorite={true} size={12} />}
import * as React from 'react';
import ComponentName from './ComponentName';
import ComponentMeasure from './ComponentMeasure';
-import ComponentLink from './ComponentLink';
import ComponentPin from './ComponentPin';
import { WorkspaceContext } from '../../../components/workspace/context';
selected = false
} = this.props;
- let componentAction = null;
-
- if (!component.refKey || component.qualifier === 'SVW') {
- switch (component.qualifier) {
- case 'FIL':
- case 'UTS':
- componentAction = (
- <WorkspaceContext.Consumer>
- {({ openComponent }) => (
- <ComponentPin
- branchLike={branchLike}
- component={component}
- openComponent={openComponent}
- />
- )}
- </WorkspaceContext.Consumer>
- );
- break;
- default:
- componentAction = <ComponentLink branchLike={branchLike} component={component} />;
- }
- }
+ const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS';
return (
<tr className={classNames({ selected })} ref={node => (this.node = node)}>
<td className="blank" />
<td className="thin nowrap">
- <span className="spacer-right">{componentAction}</span>
+ <span className="spacer-right">
+ {isFile && (
+ <WorkspaceContext.Consumer>
+ {({ openComponent }) => (
+ <ComponentPin
+ branchLike={branchLike}
+ component={component}
+ openComponent={openComponent}
+ />
+ )}
+ </WorkspaceContext.Consumer>
+ )}
+ </span>
</td>
<td className="code-name-cell">
<ComponentName
+++ /dev/null
-/*
- * 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 { Link } from 'react-router';
-import LinkIcon from '../../../components/icons-components/LinkIcon';
-import { translate } from '../../../helpers/l10n';
-import { getBranchLikeUrl } from '../../../helpers/urls';
-
-interface Props {
- branchLike?: T.BranchLike;
- component: T.ComponentMeasure;
-}
-
-export default function ComponentLink({ component, branchLike }: Props) {
- return (
- <Link
- className="link-no-underline"
- title={translate('code.open_component_page')}
- to={getBranchLikeUrl(component.refKey || component.key, branchLike)}>
- <LinkIcon />
- </Link>
- );
-}
import TreeMap, { TreeMapItem } from '../../../components/charts/TreeMap';
import { translate, translateWithParameters, getLocalizedMetricName } from '../../../helpers/l10n';
import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
-import { getBranchLikeUrl } from '../../../helpers/urls';
import { isDefined } from '../../../helpers/types';
interface Props {
this.state = { treemapItems: this.getTreemapComponents(props) };
}
- componentWillReceiveProps(nextProps: Props) {
- if (nextProps.components !== this.props.components || nextProps.metric !== this.props.metric) {
- this.setState({ treemapItems: this.getTreemapComponents(nextProps) });
+ componentDidUpdate(prevProps: Props) {
+ if (prevProps.components !== this.props.components || prevProps.metric !== this.props.metric) {
+ this.setState({ treemapItems: this.getTreemapComponents(this.props) });
}
}
- getTreemapComponents = ({ branchLike, components, metric }: Props) => {
+ getTreemapComponents = ({ components, metric }: Props) => {
const colorScale = this.getColorScale(metric);
return components
.map(component => {
icon: <QualifierIcon fill={theme.baseFontColor} qualifier={component.qualifier} />,
key: component.refKey || component.key,
label: component.name,
- link: getBranchLikeUrl(component.refKey || component.key, branchLike),
size: sizeValue,
tooltip: this.getTooltip({
colorMetric: metric,
{openIssue ? (
<div className="pull-left width-60">
<ComponentBreadcrumbs
- branchLike={this.props.branchLike}
component={component}
issue={openIssue}
organization={this.props.organization}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Link } from 'react-router';
import { getSelectedLocation } from '../utils';
import Organization from '../../../components/shared/Organization';
import { collapsePath, limitComponentName } from '../../../helpers/path';
-import { getBranchLikeUrl, getCodeUrl } from '../../../helpers/urls';
interface Props {
- branchLike?: T.BranchLike;
component?: T.Component;
issue: Pick<
T.Issue,
}
export default function ComponentBreadcrumbs({
- branchLike,
component,
- link = true,
issue,
organization,
selectedFlowIndex,
const displaySubProject = !component || !['BRC', 'DIR'].includes(component.qualifier);
const selectedLocation = getSelectedLocation(issue, selectedFlowIndex, selectedLocationIndex);
- const componentKey = selectedLocation ? selectedLocation.component : issue.component;
const componentName = selectedLocation ? selectedLocation.componentName : issue.componentLongName;
return (
<div className="component-name text-ellipsis">
- {displayOrganization && (
- <Organization
- link={link}
- linkClassName="link-no-underline"
- organizationKey={issue.organization}
- />
- )}
+ {displayOrganization && <Organization link={false} organizationKey={issue.organization} />}
{displayProject && (
<span title={issue.projectName}>
- {link ? (
- <Link className="link-no-underline" to={getBranchLikeUrl(issue.project, branchLike)}>
- {limitComponentName(issue.projectName)}
- </Link>
- ) : (
- limitComponentName(issue.projectName)
- )}
+ {limitComponentName(issue.projectName)}
<span className="slash-separator" />
</span>
)}
issue.subProject !== undefined &&
issue.subProjectName !== undefined && (
<span title={issue.subProjectName}>
- {link ? (
- <Link
- className="link-no-underline"
- to={getBranchLikeUrl(issue.subProject, branchLike)}>
- {limitComponentName(issue.subProjectName)}
- </Link>
- ) : (
- limitComponentName(issue.subProjectName)
- )}
+ {limitComponentName(issue.subProjectName)}
<span className="slash-separator" />
</span>
)}
- {link ? (
- <Link
- className="link-no-underline"
- to={getCodeUrl(issue.project, branchLike, componentKey)}>
- <span title={componentName}>{collapsePath(componentName || '')}</span>
- </Link>
- ) : (
- collapsePath(componentName || '')
- )}
+ {collapsePath(componentName || '')}
</div>
);
}
{displayComponent && (
<div className="issues-workspace-list-component note">
<ComponentBreadcrumbs
- branchLike={branchLike}
component={component}
issue={this.props.issue}
- link={false}
organization={this.props.organization}
/>
</div>
it('renders', () => {
expect(
shallow(
- <ComponentBreadcrumbs
- branchLike={undefined}
- component={undefined}
- issue={baseIssue}
- organization={undefined}
- />
+ <ComponentBreadcrumbs component={undefined} issue={baseIssue} organization={undefined} />
)
).toMatchSnapshot();
});
it('renders with sub-project', () => {
const issue = { ...baseIssue, subProject: 'sub-proj', subProjectName: 'sub-proj-name' };
expect(
- shallow(
- <ComponentBreadcrumbs
- branchLike={undefined}
- component={undefined}
- issue={issue}
- organization={undefined}
- />
- )
- ).toMatchSnapshot();
-});
-
-it('renders with branch', () => {
- const issue = { ...baseIssue, subProject: 'sub-proj', subProjectName: 'sub-proj-name' };
- const shortBranch: T.ShortLivingBranch = {
- isMain: false,
- mergeBranch: '',
- name: 'feature',
- type: 'SHORT'
- };
- expect(
- shallow(
- <ComponentBreadcrumbs
- branchLike={shortBranch}
- component={undefined}
- issue={issue}
- organization={undefined}
- />
- )
+ shallow(<ComponentBreadcrumbs component={undefined} issue={issue} organization={undefined} />)
).toMatchSnapshot();
});
className="component-name text-ellipsis"
>
<Connect(Organization)
- link={true}
- linkClassName="link-no-underline"
+ link={false}
organizationKey="org"
/>
<span
title="proj-name"
>
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "proj",
- },
- }
- }
- >
- proj-name
- </Link>
+ proj-name
<span
className="slash-separator"
/>
</span>
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/code",
- "query": Object {
- "id": "proj",
- "line": undefined,
- "selected": "comp",
- },
- }
- }
- >
- <span
- title="comp-name"
- >
- comp-name
- </span>
- </Link>
-</div>
-`;
-
-exports[`renders with branch 1`] = `
-<div
- className="component-name text-ellipsis"
->
- <Connect(Organization)
- link={true}
- linkClassName="link-no-underline"
- organizationKey="org"
- />
- <span
- title="proj-name"
- >
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "branch": "feature",
- "id": "proj",
- "resolved": "false",
- },
- }
- }
- >
- proj-name
- </Link>
- <span
- className="slash-separator"
- />
- </span>
- <span
- title="sub-proj-name"
- >
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "branch": "feature",
- "id": "sub-proj",
- "resolved": "false",
- },
- }
- }
- >
- sub-proj-name
- </Link>
- <span
- className="slash-separator"
- />
- </span>
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/code",
- "query": Object {
- "branch": "feature",
- "id": "proj",
- "line": undefined,
- "selected": "comp",
- },
- }
- }
- >
- <span
- title="comp-name"
- >
- comp-name
- </span>
- </Link>
+ comp-name
</div>
`;
className="component-name text-ellipsis"
>
<Connect(Organization)
- link={true}
- linkClassName="link-no-underline"
+ link={false}
organizationKey="org"
/>
<span
title="proj-name"
>
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "proj",
- },
- }
- }
- >
- proj-name
- </Link>
+ proj-name
<span
className="slash-separator"
/>
<span
title="sub-proj-name"
>
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "sub-proj",
- },
- }
- }
- >
- sub-proj-name
- </Link>
+ sub-proj-name
<span
className="slash-separator"
/>
</span>
- <Link
- className="link-no-underline"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/code",
- "query": Object {
- "id": "proj",
- "line": undefined,
- "selected": "comp",
- },
- }
- }
- >
- <span
- title="comp-name"
- >
- comp-name
- </span>
- </Link>
+ comp-name
</div>
`;
import { isShortLivingBranch } from '../../../helpers/branches';
import {
getShortLivingBranchUrl,
- getCodeUrl,
getProjectUrl,
getBaseUrl,
getPathUrlAsString
pathname: '/portfolio',
query: { id: component.key }
});
- } else if (this.isFile()) {
- this.props.router.replace(
- getCodeUrl(component.breadcrumbs[0].key, branchLike, component.key)
- );
} else if (isShortLivingBranch(branchLike)) {
this.props.router.replace(getShortLivingBranchUrl(component.key, branchLike.name));
}
isPortfolio = () => ['VW', 'SVW'].includes(this.props.component.qualifier);
- isFile = () => ['FIL', 'UTS'].includes(this.props.component.qualifier);
-
render() {
const { branchLike, branchLikes, component } = this.props;
- if (this.isPortfolio() || this.isFile() || isShortLivingBranch(branchLike)) {
+ if (this.isPortfolio() || isShortLivingBranch(branchLike)) {
return null;
}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { mount, shallow } from 'enzyme';
+import { shallow } from 'enzyme';
import { App } from '../App';
import { isSonarCloud } from '../../../../helpers/system';
).toBeTruthy();
});
-it('redirects on Code page for files', () => {
- const branch: T.LongLivingBranch = { isMain: false, name: 'b', type: 'LONG' };
- const newComponent = {
- ...component,
- breadcrumbs: [
- { key: 'project', name: 'Project', qualifier: 'TRK' },
- { key: 'foo', name: 'Foo', qualifier: 'DIR' }
- ],
- qualifier: 'FIL'
- };
- const replace = jest.fn();
- mount(
- <App
- branchLike={branch}
- branchLikes={[branch]}
- component={newComponent}
- onComponentChange={jest.fn()}
- router={{ replace }}
- />
- );
- expect(replace).toBeCalledWith({
- pathname: '/code',
- query: { branch: 'b', id: 'project', selected: 'foo' }
- });
-});
-
-function getWrapper(props: Partial<App['props']> = {}) {
+function getWrapper(props = {}) {
return shallow(
<App
branchLikes={[]}
{subProject != null && (
<div className="component-name-parent">
- <a
- className="link-with-icon"
- href={getPathUrlAsString(getBranchLikeUrl(subProject, this.props.branchLike))}>
- <QualifierIcon qualifier="BRC" /> <span>{subProjectName}</span>
- </a>
+ <QualifierIcon qualifier="BRC" /> <span>{subProjectName}</span>
</div>
)}
duplication.file.subProjectName && (
<div className="component-name-parent">
<QualifierIcon className="little-spacer-right" qualifier="BRC" />
- <Link to={getProjectUrl(duplication.file.subProject)}>
- {duplication.file.subProjectName}
- </Link>
+ {duplication.file.subProjectName}
</div>
)}
</>
{sourceViewerFile.subProject && (
<>
<QualifierIcon className="big-spacer-left little-spacer-right" qualifier="BRC" />
- <Link to={getBranchLikeUrl(sourceViewerFile.subProject, branchLike)}>
- {sourceViewerFile.subProjectName}
- </Link>
+ {sourceViewerFile.subProjectName}
</>
)}
</div>
- <div className="source-viewer-header-component-name">
+ <div className="source-viewer-header-component-name display-flex-center little-spacer-top">
<QualifierIcon className="little-spacer-right" qualifier={sourceViewerFile.q} />
{sourceViewerFile.path}
</div>
className="big-spacer-left little-spacer-right"
qualifier="BRC"
/>
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "branch": "feature",
- "id": "sub-project-key",
- "resolved": "false",
- },
- }
- }
- >
- Sub-Project Name
- </Link>
+ Sub-Project Name
</div>
<div
- className="source-viewer-header-component-name"
+ className="source-viewer-header-component-name display-flex-center little-spacer-top"
>
<QualifierIcon
className="little-spacer-right"
className="big-spacer-left little-spacer-right"
qualifier="BRC"
/>
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "branch": "feature",
- "id": "sub-project-key",
- "resolved": "false",
- },
- }
- }
- >
- Sub-Project Name
- </Link>
+ Sub-Project Name
</div>
<div
- className="source-viewer-header-component-name"
+ className="source-viewer-header-component-name display-flex-center little-spacer-top"
>
<QualifierIcon
className="little-spacer-right"
className="big-spacer-left little-spacer-right"
qualifier="BRC"
/>
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "branch": "feature",
- "id": "sub-project-key",
- "resolved": "false",
- },
- }
- }
- >
- Sub-Project Name
- </Link>
+ Sub-Project Name
</div>
<div
- className="source-viewer-header-component-name"
+ className="source-viewer-header-component-name display-flex-center little-spacer-top"
>
<QualifierIcon
className="little-spacer-right"
}
.source-viewer-header-component-project {
- color: var(--secondFontColor);
font-size: var(--smallFontSize);
}
.source-viewer-header-component-name {
- font-weight: 600;
+ font-size: var(--smallFontSize);
}
.source-viewer-header-favorite {
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { Dispatch } from 'redux';
+import { InjectedRouter } from 'react-router';
+import { requireAuthorization as requireAuthorizationAction } from './appState';
import { addGlobalErrorMessage } from './globalMessages';
import { receiveLanguages } from './languages';
import { receiveMetrics } from './metrics';
}
);
}
+
+export function requireAuthorization(router: Pick<InjectedRouter, 'replace'>) {
+ const returnTo = window.location.pathname + window.location.search + window.location.hash;
+ router.replace({ pathname: '/sessions/new', query: { return_to: returnTo } });
+ return requireAuthorizationAction();
+}
qualifier.DEV=Developer
qualifier.configuration.TRK=Project Configuration
-qualifier.configuration.BRC=Sub-project Configuration
-qualifier.configuration.DIR=Directory Configuration
-qualifier.configuration.PAC=Package Configuration
qualifier.configuration.VW=Portfolio Configuration
qualifier.configuration.SVW=Portfolio Configuration
qualifier.configuration.APP=Application Configuration
-qualifier.configuration.FIL=File Configuration
-qualifier.configuration.CLA=File Configuration
-qualifier.configuration.UTS=Test File Configuration
-qualifier.configuration.DEV=Developer Configuration
qualifiers.TRK=Projects
qualifiers.BRC=Sub-projects
overview.about_this_portfolio=About This Portfolio
overview.about_this_project.APP=About This Application
overview.about_this_project.TRK=About This Project
-overview.about_this_project.BRC=About This Sub-Project
-overview.about_this_project.DIR=About This Directory
overview.project_activity.APP=Application Activity
overview.project_activity.TRK=Project Activity
-overview.project_activity.BRC=Sub-Project Activity
-overview.project_activity.DIR=Directory Activity
overview.external_links=External Links
overview.project_key.APP=Application Key
overview.project_key.TRK=Project Key
-overview.project_key.BRC=Sub-Project Key
-overview.project_key.DIR=Directory Key
overview.project.no_lines_of_code=This project as no lines of code.
overview.project.empty=This project is empty.
#
#------------------------------------------------------------------------------
favorite.check.TRK=Click to mark this project as favorite.
-favorite.check.BRC=Click to mark this sub-project as favorite.
-favorite.check.DIR=Click to mark this directory as favorite.
-favorite.check.PAC=Click to mark this package as favorite.
favorite.check.VW=Click to mark this portfolio as favorite.
favorite.check.SVW=Click to mark this sub-ortfolio as favorite.
favorite.check.APP=Click to mark this application as favorite.
favorite.check.FIL=Click to mark this file as favorite.
-favorite.check.CLA=Click to mark this file as favorite.
favorite.check.UTS=Click to mark this test file as favorite.
favorite.current.TRK=This project is marked as favorite.
-favorite.current.BRC=This sub-project is marked as favorite.
-favorite.current.DIR=This directory is marked as favorite.
-favorite.current.PAC=This package is marked as favorite.
favorite.current.VW=This portfolio is marked as favorite.
favorite.current.SVW=This sub-ortfolio is marked as favorite.
favorite.current.APP=This application is marked as favorite.
favorite.current.FIL=This file is marked as favorite.
-favorite.current.CLA=This file is marked as favorite.
favorite.current.UTS=This test file is marked as favorite.