diff options
author | Wouter Admiraal <45544358+wouter-admiraal-sonarsource@users.noreply.github.com> | 2021-08-30 12:10:31 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-08-30 20:08:19 +0000 |
commit | 49789f6a622f402e85a72b9b77b9dfa827785a60 (patch) | |
tree | 065bd83c142248fdb576510da973147fbe3bf6e7 /server/sonar-web/src/main/js/apps/component-measures | |
parent | 1e78c066e5eed1752abfbcc91b977703d5dd0c51 (diff) | |
download | sonarqube-49789f6a622f402e85a72b9b77b9dfa827785a60.tar.gz sonarqube-49789f6a622f402e85a72b9b77b9dfa827785a60.zip |
SONAR-12018 Keep users on Measures page when drilling down
Diffstat (limited to 'server/sonar-web/src/main/js/apps/component-measures')
18 files changed, 595 insertions, 170 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx index a117429d6fa..c5c6b915305 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx @@ -31,11 +31,12 @@ import { RequestData } from '../../../helpers/request'; import { scrollToElement } from '../../../helpers/scrolling'; import { getProjectUrl } from '../../../helpers/urls'; import { BranchLike } from '../../../types/branch-like'; +import { MeasurePageView } from '../../../types/measures'; import { MetricKey } from '../../../types/metrics'; import { complementary } from '../config/complementary'; import FilesView from '../drilldown/FilesView'; import TreeMapView from '../drilldown/TreeMapView'; -import { enhanceComponent, isFileType, isViewType, Query, View } from '../utils'; +import { enhanceComponent, isFileType, isViewType, Query } from '../utils'; import Breadcrumbs from './Breadcrumbs'; import MeasureContentHeader from './MeasureContentHeader'; import MeasureHeader from './MeasureHeader'; @@ -51,7 +52,7 @@ interface Props { router: InjectedRouter; selected?: string; updateQuery: (query: Partial<Query>) => void; - view: View; + view: MeasurePageView; } interface State { @@ -170,7 +171,7 @@ export default class MeasureContent extends React.PureComponent<Props, State> { }; getComponentRequestParams( - view: View, + view: MeasurePageView, metric: Pick<T.Metric, 'key' | 'direction'>, options: Object = {} ) { @@ -218,7 +219,7 @@ export default class MeasureContent extends React.PureComponent<Props, State> { }); }; - updateView = (view: View) => { + updateView = (view: MeasurePageView) => { this.props.updateQuery({ view }); }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx index c6860eb3066..43da92b6dcb 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx @@ -23,13 +23,14 @@ import ListIcon from '../../../components/icons/ListIcon'; import TreeIcon from '../../../components/icons/TreeIcon'; import TreemapIcon from '../../../components/icons/TreemapIcon'; import { translate } from '../../../helpers/l10n'; -import { hasList, hasTree, hasTreemap, View } from '../utils'; +import { MeasurePageView } from '../../../types/measures'; +import { hasList, hasTree, hasTreemap } from '../utils'; interface Props { className?: string; metric: T.Metric; - handleViewChange: (view: View) => void; - view: View; + handleViewChange: (view: MeasurePageView) => void; + view: MeasurePageView; } export default class MeasureViewSelect extends React.PureComponent<Props> { @@ -61,7 +62,7 @@ export default class MeasureViewSelect extends React.PureComponent<Props> { }; handleChange = (option: { value: string }) => { - return this.props.handleViewChange(option.value as View); + return this.props.handleViewChange(option.value as MeasurePageView); }; renderOption = (option: { icon: JSX.Element; label: string }) => { diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx index 9c9ae1d407e..c805df0da81 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx @@ -21,7 +21,8 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { getMeasuresWithPeriod } from '../../../../api/measures'; import { mockMainBranch, mockPullRequest } from '../../../../helpers/mocks/branch-like'; -import { mockComponent, mockIssue, mockLocation, mockRouter } from '../../../../helpers/testMocks'; +import { mockComponent } from '../../../../helpers/mocks/component'; +import { mockIssue, mockLocation, mockRouter } from '../../../../helpers/testMocks'; import { waitAndUpdate } from '../../../../helpers/testUtils'; import { App } from '../App'; diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx index d89eb1f0c04..1071d5f1fba 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureContent-test.tsx @@ -20,8 +20,9 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { getComponentTree } from '../../../../api/components'; +import { mockComponentMeasure } from '../../../../helpers/mocks/component'; import { scrollToElement } from '../../../../helpers/scrolling'; -import { mockComponentMeasure, mockRouter } from '../../../../helpers/testMocks'; +import { mockRouter } from '../../../../helpers/testMocks'; import { waitAndUpdate } from '../../../../helpers/testUtils'; import MeasureContent from '../MeasureContent'; @@ -30,7 +31,7 @@ jest.mock('../../../../helpers/scrolling', () => ({ })); jest.mock('../../../../api/components', () => { - const { mockComponentMeasure } = jest.requireActual('../../../../helpers/testMocks'); + const { mockComponentMeasure } = jest.requireActual('../../../../helpers/mocks/component'); return { getComponentTree: jest.fn().mockResolvedValue({ paging: { pageIndex: 1, pageSize: 500, total: 2 }, diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx index 77923e3f982..4d368739eb1 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureOverview-test.tsx @@ -23,9 +23,11 @@ import { keyBy } from 'lodash'; import * as React from 'react'; import { getComponentLeaves } from '../../../../api/components'; import { mockBranch } from '../../../../helpers/mocks/branch-like'; -import { mockComponentMeasureEnhanced } from '../../../../helpers/mocks/component'; import { mockComponentMeasure, + mockComponentMeasureEnhanced +} from '../../../../helpers/mocks/component'; +import { mockMeasure, mockMeasureEnhanced, mockMetric, diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx index 161f47f2a34..302bf0bf71c 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureViewSelect-test.tsx @@ -17,18 +17,34 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + import { shallow } from 'enzyme'; import * as React from 'react'; +import { mockMetric } from '../../../../helpers/testMocks'; +import { MetricKey } from '../../../../types/metrics'; import MeasureViewSelect from '../MeasureViewSelect'; -it('should display correctly with treemap option', () => { +it('should render correctly', () => { + expect( + shallowRender({ metric: mockMetric({ key: MetricKey.releasability_rating }) }) + ).toMatchSnapshot('has no list'); expect( - shallow( - <MeasureViewSelect - handleViewChange={() => {}} - metric={{ type: 'PERCENT' } as T.Metric} - view="tree" - /> - ) - ).toMatchSnapshot(); + shallowRender({ metric: mockMetric({ key: MetricKey.alert_status, type: 'LEVEL' }) }) + ).toMatchSnapshot('has no tree'); + expect(shallowRender({ metric: mockMetric({ type: 'RATING' }) })).toMatchSnapshot( + 'has no treemap' + ); }); + +it('should correctly trigger a selection change', () => { + const handleViewChange = jest.fn(); + const wrapper = shallowRender({ handleViewChange }); + wrapper.instance().handleChange({ value: 'list' }); + expect(handleViewChange).toBeCalledWith('list'); +}); + +function shallowRender(props: Partial<MeasureViewSelect['props']> = {}) { + return shallow<MeasureViewSelect>( + <MeasureViewSelect metric={mockMetric()} handleViewChange={jest.fn()} view="list" {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap index bc440003070..29fe3dbd9eb 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureViewSelect-test.tsx.snap @@ -1,6 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should display correctly with treemap option 1`] = ` +exports[`should render correctly: has no list 1`] = ` +<Select + autoBlur={true} + clearable={false} + onChange={[Function]} + optionRenderer={[Function]} + options={ + Array [ + Object { + "icon": <TreeIcon />, + "label": "component_measures.tab.tree", + "value": "tree", + }, + Object { + "icon": <TreemapIcon />, + "label": "component_measures.tab.treemap", + "value": "treemap", + }, + ] + } + searchable={false} + value="list" + valueRenderer={[Function]} +/> +`; + +exports[`should render correctly: has no tree 1`] = ` +<Select + autoBlur={true} + clearable={false} + onChange={[Function]} + optionRenderer={[Function]} + options={ + Array [ + Object { + "icon": <ListIcon />, + "label": "component_measures.tab.list", + "value": "list", + }, + ] + } + searchable={false} + value="list" + valueRenderer={[Function]} +/> +`; + +exports[`should render correctly: has no treemap 1`] = ` <Select autoBlur={true} clearable={false} @@ -26,7 +73,7 @@ exports[`should display correctly with treemap option 1`] = ` ] } searchable={false} - value="tree" + value="list" valueRenderer={[Function]} /> `; diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx index 7a5e0cf70d5..9b81111a7db 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.tsx @@ -17,123 +17,113 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { LocationDescriptor } from 'history'; import * as React from 'react'; import { Link } from 'react-router'; import BranchIcon from '../../../components/icons/BranchIcon'; import LinkIcon from '../../../components/icons/LinkIcon'; import QualifierIcon from '../../../components/icons/QualifierIcon'; import { translate } from '../../../helpers/l10n'; -import { isDiffMetric } from '../../../helpers/measures'; import { splitPath } from '../../../helpers/path'; import { getBranchLikeUrl, getComponentDrilldownUrlWithSelection, - getComponentSecurityHotspotsUrl, getProjectUrl } from '../../../helpers/urls'; import { BranchLike } from '../../../types/branch-like'; -import { isFileType, isSecurityReviewMetric, View } from '../utils'; +import { + ComponentQualifier, + isApplication, + isPortfolioLike, + isProject +} from '../../../types/component'; +import { MeasurePageView } from '../../../types/measures'; +import { MetricKey } from '../../../types/metrics'; -interface Props { +export interface ComponentCellProps { branchLike?: BranchLike; component: T.ComponentMeasureEnhanced; - onClick: (component: string) => void; metric: T.Metric; rootComponent: T.ComponentMeasure; - view: View; + view: MeasurePageView; } -export default class ComponentCell extends React.PureComponent<Props> { - handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => { - const isLeftClickEvent = event.button === 0; - const isModifiedEvent = !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); +export default function ComponentCell(props: ComponentCellProps) { + const { branchLike, component, metric, rootComponent, view } = props; - if (isLeftClickEvent && !isModifiedEvent) { - event.preventDefault(); - this.props.onClick(this.props.component.key); - } - }; + let head = ''; + let tail = component.name; - renderInner(componentKey: string) { - const { component } = this.props; - let head = ''; - let tail = component.name; + if ( + view === 'list' && + ([ + ComponentQualifier.File, + ComponentQualifier.TestFile, + ComponentQualifier.Directory + ] as string[]).includes(component.qualifier) && + component.path + ) { + ({ head, tail } = splitPath(component.path)); + } + let path: LocationDescriptor; + if (component.refKey) { if ( - this.props.view === 'list' && - ['FIL', 'UTS', 'DIR'].includes(component.qualifier) && - component.path + !isPortfolioLike(component.qualifier) && + ([MetricKey.releasability_rating, MetricKey.alert_status] as string[]).includes(metric.key) ) { - ({ head, tail } = splitPath(component.path)); + path = isApplication(component.qualifier) + ? getProjectUrl(component.refKey, component.branch) + : getBranchLikeUrl(component.refKey, branchLike); + } else if (isProject(component.qualifier) && metric.key === MetricKey.projects) { + path = getBranchLikeUrl(component.refKey, branchLike); + } else { + path = getComponentDrilldownUrlWithSelection( + component.refKey, + '', + metric.key, + branchLike, + view + ); } - - const isApp = this.props.rootComponent.qualifier === 'APP'; - - return ( - <span title={componentKey}> - <QualifierIcon className="little-spacer-right" qualifier={component.qualifier} /> - {head.length > 0 && <span className="note">{head}/</span>} - <span>{tail}</span> - {isApp && - (component.branch ? ( - <> - <BranchIcon className="spacer-left little-spacer-right" /> - <span className="note">{component.branch}</span> - </> - ) : ( - <span className="spacer-left badge">{translate('branches.main_branch')}</span> - ))} - </span> + } else { + path = getComponentDrilldownUrlWithSelection( + rootComponent.key, + component.key, + metric.key, + branchLike, + view ); } - render() { - const { branchLike, component, metric, rootComponent } = this.props; - - let hotspotsUrl; - if (isFileType(component) && isSecurityReviewMetric(metric.key)) { - hotspotsUrl = getComponentSecurityHotspotsUrl(this.props.rootComponent.key, { - file: component.path, - sinceLeakPeriod: isDiffMetric(metric.key) ? 'true' : undefined - }); - } - - return ( - <td className="measure-details-component-cell"> - <div className="text-ellipsis"> - {!component.refKey ? ( - <Link - className="link-no-underline" - to={ - hotspotsUrl || - getComponentDrilldownUrlWithSelection( - rootComponent.key, - component.key, - metric.key, - branchLike - ) - } - id={'component-measures-component-link-' + component.key} - onClick={hotspotsUrl ? undefined : this.handleClick}> - {this.renderInner(component.key)} - </Link> - ) : ( - <Link - className="link-no-underline" - id={'component-measures-component-link-' + component.refKey} - to={ - this.props.rootComponent.qualifier === 'APP' - ? getProjectUrl(component.refKey, component.branch) - : getBranchLikeUrl(component.refKey, branchLike) - }> - <span className="big-spacer-right"> - <LinkIcon /> - </span> - {this.renderInner(component.refKey)} - </Link> + return ( + <td className="measure-details-component-cell"> + <div className="text-ellipsis"> + <Link + className="link-no-underline" + to={path} + id={'component-measures-component-link-' + component.key}> + {component.refKey && ( + <span className="big-spacer-right"> + <LinkIcon /> + </span> )} - </div> - </td> - ); - } + <span title={component.key}> + <QualifierIcon className="little-spacer-right" qualifier={component.qualifier} /> + {head.length > 0 && <span className="note">{head}/</span>} + <span>{tail}</span> + {isApplication(rootComponent.qualifier) && + (component.branch ? ( + <> + <BranchIcon className="spacer-left little-spacer-right" /> + <span className="note">{component.branch}</span> + </> + ) : ( + <span className="spacer-left badge">{translate('branches.main_branch')}</span> + ))} + </span> + </Link> + </div> + </td> + ); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx index 1bd16bccfbe..907f671a76f 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.tsx @@ -20,20 +20,19 @@ import * as React from 'react'; import { getLocalizedMetricName } from '../../../helpers/l10n'; import { BranchLike } from '../../../types/branch-like'; +import { MeasurePageView } from '../../../types/measures'; import { complementary } from '../config/complementary'; -import { View } from '../utils'; import ComponentsListRow from './ComponentsListRow'; import EmptyResult from './EmptyResult'; interface Props { branchLike?: BranchLike; components: T.ComponentMeasureEnhanced[]; - onClick: (component: string) => void; metric: T.Metric; metrics: T.Dict<T.Metric>; rootComponent: T.ComponentMeasure; selectedComponent?: string; - view: View; + view: MeasurePageView; } export default function ComponentsList({ components, metric, metrics, ...props }: Props) { diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx index b1749e548f5..c29be3db951 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.tsx @@ -20,7 +20,7 @@ import * as classNames from 'classnames'; import * as React from 'react'; import { BranchLike } from '../../../types/branch-like'; -import { View } from '../utils'; +import { MeasurePageView } from '../../../types/measures'; import ComponentCell from './ComponentCell'; import MeasureCell from './MeasureCell'; @@ -28,11 +28,10 @@ interface Props { branchLike?: BranchLike; component: T.ComponentMeasureEnhanced; isSelected: boolean; - onClick: (component: string) => void; otherMetrics: T.Metric[]; metric: T.Metric; rootComponent: T.ComponentMeasure; - view: View; + view: MeasurePageView; } export default function ComponentsListRow(props: Props) { @@ -50,7 +49,6 @@ export default function ComponentsListRow(props: Props) { branchLike={branchLike} component={component} metric={props.metric} - onClick={props.onClick} rootComponent={rootComponent} view={props.view} /> diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx index 828bad17a29..8e6271ea845 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx @@ -27,7 +27,7 @@ import { translate, translateWithParameters } from '../../../helpers/l10n'; import { formatMeasure, isDiffMetric, isPeriodBestValue } from '../../../helpers/measures'; import { scrollToElement } from '../../../helpers/scrolling'; import { BranchLike } from '../../../types/branch-like'; -import { View } from '../utils'; +import { MeasurePageView } from '../../../types/measures'; import ComponentsList from './ComponentsList'; interface Props { @@ -44,7 +44,7 @@ interface Props { rootComponent: T.ComponentMeasure; selectedKey?: string; selectedIdx?: number; - view: View; + view: MeasurePageView; } interface State { @@ -173,7 +173,6 @@ export default class FilesView extends React.PureComponent<Props, State> { components={filteredComponents} metric={this.props.metric} metrics={this.props.metrics} - onClick={this.props.handleOpen} rootComponent={this.props.rootComponent} selectedComponent={this.props.selectedKey} view={this.props.view} diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx index 4673a98f943..45a67de0058 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/BubbleChart-test.tsx @@ -21,12 +21,8 @@ import { shallow } from 'enzyme'; import { keyBy } from 'lodash'; import * as React from 'react'; import OriginalBubbleChart from '../../../../components/charts/BubbleChart'; -import { - mockComponentMeasure, - mockMeasure, - mockMetric, - mockPaging -} from '../../../../helpers/testMocks'; +import { mockComponentMeasure } from '../../../../helpers/mocks/component'; +import { mockMeasure, mockMetric, mockPaging } from '../../../../helpers/testMocks'; import { MetricKey } from '../../../../types/metrics'; import { enhanceComponent } from '../../utils'; import BubbleChart from '../BubbleChart'; diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx index 981ef5a8bbe..ccb196e14b8 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentCell-test.tsx @@ -19,31 +19,104 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { mockComponentMeasure, mockMetric } from '../../../../helpers/testMocks'; +import { mockBranch, mockPullRequest } from '../../../../helpers/mocks/branch-like'; +import { + mockComponentMeasure, + mockComponentMeasureEnhanced +} from '../../../../helpers/mocks/component'; +import { mockMetric } from '../../../../helpers/testMocks'; +import { ComponentQualifier } from '../../../../types/component'; import { MetricKey } from '../../../../types/metrics'; import { enhanceComponent } from '../../utils'; -import ComponentCell from '../ComponentCell'; +import ComponentCell, { ComponentCellProps } from '../ComponentCell'; it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender({}, MetricKey.security_hotspots)).toMatchSnapshot('security review domain'); - - const metric = mockMetric({ key: MetricKey.bugs }); expect( shallowRender({ - component: enhanceComponent( - mockComponentMeasure(false, { refKey: 'project-key' }), - { key: metric.key }, - { [metric.key]: metric } - ) + rootComponent: mockComponentMeasure(false, { qualifier: ComponentQualifier.Application }) + }) + ).toMatchSnapshot('root component is application, component is on main branch'); + expect( + shallowRender({ + rootComponent: mockComponentMeasure(false, { qualifier: ComponentQualifier.Application }), + component: mockComponentMeasureEnhanced({ branch: 'develop' }) + }) + ).toMatchSnapshot('root component is application, component has branch'); + expect( + shallowRender({ component: mockComponentMeasureEnhanced({ refKey: 'project-key' }) }) + ).toMatchSnapshot('ref project component'); + expect( + shallowRender( + { + component: mockComponentMeasureEnhanced({ + refKey: 'project-key', + qualifier: ComponentQualifier.Project + }), + branchLike: mockBranch() + }, + MetricKey.releasability_rating + ) + ).toMatchSnapshot('ref project component, releasability metric'); + expect( + shallowRender( + { + component: mockComponentMeasureEnhanced({ + refKey: 'app-key', + qualifier: ComponentQualifier.Application + }), + branchLike: mockBranch() + }, + MetricKey.projects + ) + ).toMatchSnapshot('ref application component, projects'); + expect( + shallowRender( + { + component: mockComponentMeasureEnhanced({ + refKey: 'project-key', + qualifier: ComponentQualifier.Project + }), + branchLike: mockBranch() + }, + MetricKey.projects + ) + ).toMatchSnapshot('ref project component, projects'); + expect( + shallowRender( + { + component: mockComponentMeasureEnhanced({ + refKey: 'app-key', + qualifier: ComponentQualifier.Application + }), + branchLike: mockPullRequest() + }, + MetricKey.alert_status + ) + ).toMatchSnapshot('ref application component, alert_status metric'); + expect( + shallowRender( + { + component: mockComponentMeasureEnhanced({ + refKey: 'vw-key', + qualifier: ComponentQualifier.Portfolio + }), + branchLike: mockPullRequest() + }, + MetricKey.alert_status + ) + ).toMatchSnapshot('ref portfolio component, alert_status metric'); + expect( + shallowRender({ + component: mockComponentMeasureEnhanced({ + key: 'svw-bar', + qualifier: ComponentQualifier.SubPortfolio + }) }) - ).toMatchSnapshot('ref component'); + ).toMatchSnapshot('sub-portfolio component'); }); -function shallowRender( - overrides: Partial<ComponentCell['props']> = {}, - metricKey = MetricKey.bugs -) { +function shallowRender(overrides: Partial<ComponentCellProps> = {}, metricKey = MetricKey.bugs) { const metric = mockMetric({ key: metricKey }); const component = enhanceComponent( mockComponentMeasure(true, { @@ -53,11 +126,10 @@ function shallowRender( { [metric.key]: metric } ); - return shallow<ComponentCell>( + return shallow<ComponentCellProps>( <ComponentCell component={component} metric={metric} - onClick={jest.fn()} rootComponent={mockComponentMeasure(false)} view="list" {...overrides} diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx index bbefdc42480..478ab794bd0 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx @@ -44,7 +44,6 @@ it('should renders correctly', () => { components={COMPONENTS} metric={METRICS.new_bugs} metrics={METRICS} - onClick={jest.fn()} rootComponent={COMPONENTS[0]} view="tree" /> @@ -59,7 +58,6 @@ it('should renders empty', () => { components={[]} metric={METRICS.new_bugs} metrics={METRICS} - onClick={jest.fn()} rootComponent={COMPONENTS[0]} view="tree" /> @@ -74,7 +72,6 @@ it('should renders with multiple measures', () => { components={COMPONENTS} metric={METRICS.coverage} metrics={METRICS} - onClick={jest.fn()} rootComponent={COMPONENTS[0]} view="tree" /> diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap index 21d4106dec4..474ada97872 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentCell-test.tsx.snap @@ -10,7 +10,6 @@ exports[`should render correctly: default 1`] = ` <Link className="link-no-underline" id="component-measures-component-link-foo:src/index.tsx" - onClick={[Function]} onlyActiveOnIndex={false} style={Object {}} to={ @@ -20,6 +19,7 @@ exports[`should render correctly: default 1`] = ` "id": "foo", "metric": "bugs", "selected": "foo:src/index.tsx", + "view": "list", }, } } @@ -46,7 +46,7 @@ exports[`should render correctly: default 1`] = ` </td> `; -exports[`should render correctly: ref component 1`] = ` +exports[`should render correctly: ref application component, alert_status metric 1`] = ` <td className="measure-details-component-cell" > @@ -55,7 +55,7 @@ exports[`should render correctly: ref component 1`] = ` > <Link className="link-no-underline" - id="component-measures-component-link-project-key" + id="component-measures-component-link-foo" onlyActiveOnIndex={false} style={Object {}} to={ @@ -63,7 +63,141 @@ exports[`should render correctly: ref component 1`] = ` "pathname": "/dashboard", "query": Object { "branch": undefined, + "id": "app-key", + }, + } + } + > + <span + className="big-spacer-right" + > + <LinkIcon /> + </span> + <span + title="foo" + > + <QualifierIcon + className="little-spacer-right" + qualifier="APP" + /> + <span> + Foo + </span> + </span> + </Link> + </div> +</td> +`; + +exports[`should render correctly: ref application component, projects 1`] = ` +<td + className="measure-details-component-cell" +> + <div + className="text-ellipsis" + > + <Link + className="link-no-underline" + id="component-measures-component-link-foo" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/component_measures", + "query": Object { + "branch": "branch-6.7", + "id": "app-key", + "metric": "projects", + "view": "list", + }, + } + } + > + <span + className="big-spacer-right" + > + <LinkIcon /> + </span> + <span + title="foo" + > + <QualifierIcon + className="little-spacer-right" + qualifier="APP" + /> + <span> + Foo + </span> + </span> + </Link> + </div> +</td> +`; + +exports[`should render correctly: ref portfolio component, alert_status metric 1`] = ` +<td + className="measure-details-component-cell" +> + <div + className="text-ellipsis" + > + <Link + className="link-no-underline" + id="component-measures-component-link-foo" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/component_measures", + "query": Object { + "id": "vw-key", + "metric": "alert_status", + "pullRequest": "1001", + "view": "list", + }, + } + } + > + <span + className="big-spacer-right" + > + <LinkIcon /> + </span> + <span + title="foo" + > + <QualifierIcon + className="little-spacer-right" + qualifier="VW" + /> + <span> + Foo + </span> + </span> + </Link> + </div> +</td> +`; + +exports[`should render correctly: ref project component 1`] = ` +<td + className="measure-details-component-cell" +> + <div + className="text-ellipsis" + > + <Link + className="link-no-underline" + id="component-measures-component-link-foo" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/component_measures", + "query": Object { "id": "project-key", + "metric": "bugs", + "view": "list", }, } } @@ -74,7 +208,7 @@ exports[`should render correctly: ref component 1`] = ` <LinkIcon /> </span> <span - title="project-key" + title="foo" > <QualifierIcon className="little-spacer-right" @@ -89,7 +223,141 @@ exports[`should render correctly: ref component 1`] = ` </td> `; -exports[`should render correctly: security review domain 1`] = ` +exports[`should render correctly: ref project component, projects 1`] = ` +<td + className="measure-details-component-cell" +> + <div + className="text-ellipsis" + > + <Link + className="link-no-underline" + id="component-measures-component-link-foo" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/dashboard", + "query": Object { + "branch": "branch-6.7", + "id": "project-key", + }, + } + } + > + <span + className="big-spacer-right" + > + <LinkIcon /> + </span> + <span + title="foo" + > + <QualifierIcon + className="little-spacer-right" + qualifier="TRK" + /> + <span> + Foo + </span> + </span> + </Link> + </div> +</td> +`; + +exports[`should render correctly: ref project component, releasability metric 1`] = ` +<td + className="measure-details-component-cell" +> + <div + className="text-ellipsis" + > + <Link + className="link-no-underline" + id="component-measures-component-link-foo" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/dashboard", + "query": Object { + "branch": "branch-6.7", + "id": "project-key", + }, + } + } + > + <span + className="big-spacer-right" + > + <LinkIcon /> + </span> + <span + title="foo" + > + <QualifierIcon + className="little-spacer-right" + qualifier="TRK" + /> + <span> + Foo + </span> + </span> + </Link> + </div> +</td> +`; + +exports[`should render correctly: root component is application, component has branch 1`] = ` +<td + className="measure-details-component-cell" +> + <div + className="text-ellipsis" + > + <Link + className="link-no-underline" + id="component-measures-component-link-foo" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/component_measures", + "query": Object { + "id": "foo", + "metric": "bugs", + "selected": "foo", + "view": "list", + }, + } + } + > + <span + title="foo" + > + <QualifierIcon + className="little-spacer-right" + qualifier="TRK" + /> + <span> + Foo + </span> + <BranchIcon + className="spacer-left little-spacer-right" + /> + <span + className="note" + > + develop + </span> + </span> + </Link> + </div> +</td> +`; + +exports[`should render correctly: root component is application, component is on main branch 1`] = ` <td className="measure-details-component-cell" > @@ -103,15 +371,12 @@ exports[`should render correctly: security review domain 1`] = ` style={Object {}} to={ Object { - "pathname": "/security_hotspots", + "pathname": "/component_measures", "query": Object { - "assignedToMe": undefined, - "branch": undefined, - "file": "src/index.tsx", - "hotspots": undefined, "id": "foo", - "pullRequest": undefined, - "sinceLeakPeriod": undefined, + "metric": "bugs", + "selected": "foo:src/index.tsx", + "view": "list", }, } } @@ -132,6 +397,51 @@ exports[`should render correctly: security review domain 1`] = ` <span> index.tsx </span> + <span + className="spacer-left badge" + > + branches.main_branch + </span> + </span> + </Link> + </div> +</td> +`; + +exports[`should render correctly: sub-portfolio component 1`] = ` +<td + className="measure-details-component-cell" +> + <div + className="text-ellipsis" + > + <Link + className="link-no-underline" + id="component-measures-component-link-svw-bar" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/component_measures", + "query": Object { + "id": "foo", + "metric": "bugs", + "selected": "svw-bar", + "view": "list", + }, + } + } + > + <span + title="svw-bar" + > + <QualifierIcon + className="little-spacer-right" + qualifier="SVW" + /> + <span> + Foo + </span> </span> </Link> </div> diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap index eee4db08dc2..85f717ade47 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap @@ -24,7 +24,6 @@ exports[`should renders correctly 1`] = ` "type": "INT", } } - onClick={[MockFunction]} otherMetrics={Array []} rootComponent={ Object { @@ -102,7 +101,6 @@ exports[`should renders with multiple measures 1`] = ` "type": "PERCENT", } } - onClick={[MockFunction]} otherMetrics={ Array [ Object { diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap index edf84eb575e..75f52aa9974 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap @@ -31,7 +31,6 @@ exports[`should render with best values hidden 1`] = ` }, } } - onClick={[MockFunction]} rootComponent={ Object { "key": "parent", @@ -92,7 +91,6 @@ exports[`should renders correctly 1`] = ` }, } } - onClick={[MockFunction]} rootComponent={ Object { "key": "parent", diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.ts b/server/sonar-web/src/main/js/apps/component-measures/utils.ts index a36aaf2a613..5b95979165e 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/utils.ts +++ b/server/sonar-web/src/main/js/apps/component-measures/utils.ts @@ -25,15 +25,14 @@ import { getDisplayMetrics, isDiffMetric } from '../../helpers/measures'; import { cleanQuery, parseAsString, serializeString } from '../../helpers/query'; import { BranchLike } from '../../types/branch-like'; import { ComponentQualifier } from '../../types/component'; +import { MeasurePageView } from '../../types/measures'; import { MetricKey } from '../../types/metrics'; import { bubbles } from './config/bubbles'; import { domains } from './config/domains'; -export type View = 'list' | 'tree' | 'treemap'; - export const BUBBLES_FETCH_LIMIT = 500; export const PROJECT_OVERVEW = 'project_overview'; -export const DEFAULT_VIEW: View = 'tree'; +export const DEFAULT_VIEW: MeasurePageView = 'tree'; export const DEFAULT_METRIC = PROJECT_OVERVEW; export const KNOWN_DOMAINS = [ 'Releasability', @@ -212,8 +211,8 @@ export function isProjectOverview(metric: string) { return metric === PROJECT_OVERVEW; } -function parseView(metric: string, rawView?: string): View { - const view = (parseAsString(rawView) || DEFAULT_VIEW) as View; +function parseView(metric: string, rawView?: string): MeasurePageView { + const view = (parseAsString(rawView) || DEFAULT_VIEW) as MeasurePageView; if (!hasTree(metric)) { return 'list'; } else if (view === 'list' && !hasList(metric)) { @@ -225,7 +224,7 @@ function parseView(metric: string, rawView?: string): View { export interface Query { metric: string; selected?: string; - view: View; + view: MeasurePageView; } export const parseQuery = memoize( |