From 7509e0895c7db3c0c8f5715a9b273cb81262e268 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Wed, 15 Aug 2018 15:52:22 +0200 Subject: [PATCH] SONAR-11140 Don't automatically select first file in project measures page Also: * Fix hidden selection when selecting a hidden file (best value) * Fix load more spinner --- server/sonar-web/src/main/js/api/measures.ts | 10 +- server/sonar-web/src/main/js/app/types.ts | 53 ++++--- .../sonar-web/src/main/js/apps/code/types.ts | 3 +- .../components/MeasureContent.tsx | 36 ++--- ...ntainer.js => MeasureContentContainer.tsx} | 101 +++++++------ .../apps/component-measures/config/bubbles.ts | 2 +- .../drilldown/ComponentsList.tsx | 124 +++++----------- .../drilldown/FilesView.tsx | 79 ++++++++-- .../drilldown/TreeMapView.tsx | 9 +- .../__tests__/ComponentList-test.tsx | 82 ++++++++++ .../drilldown/__tests__/FilesView-test.tsx | 79 ++++++++++ .../__snapshots__/ComponentList-test.tsx.snap | 140 ++++++++++++++++++ .../__snapshots__/FilesView-test.tsx.snap | 110 ++++++++++++++ .../main/js/apps/component-measures/utils.ts | 8 +- .../apps/overview/components/OverviewApp.tsx | 4 +- .../main/js/apps/overview/main/enhance.tsx | 5 +- .../js/apps/overview/meta/MetaContainer.tsx | 4 +- .../main/js/apps/overview/meta/MetaSize.tsx | 4 +- .../components/MeasuresOverlay.tsx | 3 +- .../src/main/js/components/measure/utils.ts | 9 +- .../sonar-web/src/main/js/helpers/measures.ts | 23 +-- 21 files changed, 650 insertions(+), 238 deletions(-) rename server/sonar-web/src/main/js/apps/component-measures/components/{MeasureContentContainer.js => MeasureContentContainer.tsx} (67%) create mode 100644 server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap diff --git a/server/sonar-web/src/main/js/api/measures.ts b/server/sonar-web/src/main/js/api/measures.ts index b5d1e649525..1102c39870c 100644 --- a/server/sonar-web/src/main/js/api/measures.ts +++ b/server/sonar-web/src/main/js/api/measures.ts @@ -19,8 +19,14 @@ */ import { getJSON, RequestData, postJSON, post } from '../helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; -import { Measure, MeasurePeriod } from '../helpers/measures'; -import { Metric, CustomMeasure, Paging, BranchParameters } from '../app/types'; +import { + Metric, + CustomMeasure, + Paging, + BranchParameters, + Measure, + MeasurePeriod +} from '../app/types'; import { Period } from '../helpers/periods'; export function getMeasures( diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index 8a1da4194d6..62ff786b531 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -1,5 +1,3 @@ -import { Measure, MeasureEnhanced } from '../helpers/measures'; - /* * SonarQube * Copyright (C) 2009-2018 SonarSource SA @@ -101,6 +99,28 @@ export interface ComponentQualityProfile { name: string; } +interface ComponentMeasureIntern { + isFavorite?: boolean; + isRecentlyBrowsed?: boolean; + key: string; + match?: string; + name: string; + organization?: string; + project?: string; + qualifier: string; + refKey?: string; +} + +export interface ComponentMeasure extends ComponentMeasureIntern { + measures?: Measure[]; +} + +export interface ComponentMeasureEnhanced extends ComponentMeasureIntern { + value?: string; + leak?: string; + measures: MeasureEnhanced[]; +} + export interface Condition { error: string; id: number; @@ -342,26 +362,25 @@ export interface MainBranch extends Branch { status?: { qualityGateStatus: string }; } -interface ComponentMeasureIntern { - isFavorite?: boolean; - isRecentlyBrowsed?: boolean; - key: string; - match?: string; - name: string; - organization?: string; - project?: string; - qualifier: string; - refKey?: string; +export interface MeasurePeriod { + bestValue?: boolean; + index: number; + value: string; } -export interface ComponentMeasure extends ComponentMeasureIntern { - measures?: Measure[]; +interface MeasureIntern { + bestValue?: boolean; + periods?: MeasurePeriod[]; + value?: string; } -export interface ComponentMeasureEnhanced extends ComponentMeasureIntern { - value?: string; +export interface Measure extends MeasureIntern { + metric: string; +} + +export interface MeasureEnhanced extends MeasureIntern { + metric: Metric; leak?: string; - measures: MeasureEnhanced[]; } export interface Metric { diff --git a/server/sonar-web/src/main/js/apps/code/types.ts b/server/sonar-web/src/main/js/apps/code/types.ts index a0b6459a3d3..f8fbc5af202 100644 --- a/server/sonar-web/src/main/js/apps/code/types.ts +++ b/server/sonar-web/src/main/js/apps/code/types.ts @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -import { Measure } from '../../helpers/measures'; +import { Measure } from '../../app/types'; export interface Component extends Breadcrumb { branch?: string; 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 6bdd4d6f190..9472307bc7e 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 @@ -33,15 +33,18 @@ import { getComponentTree } from '../../../api/components'; import { complementary } from '../config/complementary'; import { enhanceComponent, isFileType, isViewType } from '../utils'; import { getProjectUrl } from '../../../helpers/urls'; -import { isDiffMetric, MeasureEnhanced } from '../../../helpers/measures'; +import { isDiffMetric } from '../../../helpers/measures'; import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; import { + BranchLike, ComponentMeasure, ComponentMeasureEnhanced, - BranchLike, + CurrentUser, + isLoggedIn, Metric, - Paging + Paging, + MeasureEnhanced } from '../../../app/types'; import { RequestData } from '../../../helpers/request'; import { Period } from '../../../helpers/periods'; @@ -50,8 +53,9 @@ interface Props { branchLike?: BranchLike; className?: string; component: ComponentMeasure; - currentUser: { isLoggedIn: boolean }; + currentUser: CurrentUser; loading: boolean; + loadingMore: boolean; leakPeriod?: Period; measure?: MeasureEnhanced; metric: Metric; @@ -66,7 +70,6 @@ interface Props { } interface State { - bestValue?: string; components: ComponentMeasureEnhanced[]; metric?: Metric; paging?: Paging; @@ -147,16 +150,15 @@ export default class MeasureContent extends React.PureComponent { if (metric === this.props.metric) { if (this.mounted) { this.setState(({ selected }: State) => ({ - bestValue: r.metrics[0].bestValue, components: r.components.map(component => enhanceComponent(component, metric, metrics) ), - metric, + metric: { ...metric, ...r.metrics.find(m => m.key === metric.key) }, paging: r.paging, selected: - r.components.length > 0 && !r.components.find(c => c.key === selected) - ? r.components[0].key - : selected, + r.components.length > 0 && r.components.find(c => c.key === selected) + ? selected + : undefined, view })); } @@ -176,26 +178,25 @@ export default class MeasureContent extends React.PureComponent { const { metricKeys, opts, strategy } = this.getComponentRequestParams(view, metric, { p: paging.pageIndex + 1 }); - this.props.updateLoading({ components: true }); + this.props.updateLoading({ moreComponents: true }); getComponentTree(strategy, component.key, metricKeys, opts).then( r => { if (metric === this.props.metric) { if (this.mounted) { this.setState(state => ({ - bestValue: r.metrics[0].bestValue, components: [ ...state.components, ...r.components.map(component => enhanceComponent(component, metric, metrics)) ], - metric, + metric: { ...metric, ...r.metrics.find(m => m.key === metric.key) }, paging: r.paging, view })); } - this.props.updateLoading({ components: false }); + this.props.updateLoading({ moreComponents: false }); } }, - () => this.props.updateLoading({ components: false }) + () => this.props.updateLoading({ moreComponents: false }) ); }; @@ -243,12 +244,12 @@ export default class MeasureContent extends React.PureComponent { const selectedIdx = this.getSelectedIndex(); return ( { render() { const { branchLike, component, currentUser, measure, metric, rootComponent, view } = this.props; - const isLoggedIn = currentUser && currentUser.isLoggedIn; const isFile = isFileType(component); const selectedIdx = this.getSelectedIndex(); return ( @@ -295,7 +295,7 @@ export default class MeasureContent extends React.PureComponent { rootComponent={rootComponent} /> {component.key !== rootComponent.key && - isLoggedIn && ( + isLoggedIn(currentUser) && ( , - branchLike?: { id?: string; name: string } - ) => Promise<{ component: Component, measures: Array }>, - leakPeriod?: Period, - metric: Metric, - metrics: { [string]: Metric }, - router: { - push: ({ pathname: string, query?: RawQuery }) => void - }, - selected: ?string, - updateQuery: Query => void, - view: string -|}; */ + metricsKey: string[], + branchLike?: BranchLike + ) => Promise<{ component: ComponentMeasure; measures: MeasureEnhanced[] }>; + leakPeriod?: Period; + metric: Metric; + metrics: { [metric: string]: Metric }; + router: InjectedRouter; + selected?: string; + updateQuery: (query: Partial) => void; + view: string; +} -/*:: type State = { - component: ?Component, - loading: { - measure: boolean, - components: boolean - }, - measure: ?MeasureEnhanced, - secondaryMeasure: ?MeasureEnhanced -}; */ +interface LoadingState { + measure: boolean; + components: boolean; + moreComponents: boolean; +} -export default class MeasureContentContainer extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - state /*: State */ = { - component: null, - loading: { - measure: false, - components: false - }, - measure: null, - secondaryMeasure: null - }; +interface State { + component?: ComponentMeasure; + loading: LoadingState; + measure?: MeasureEnhanced; + secondaryMeasure?: MeasureEnhanced; +} + +export default class MeasureContentContainer extends React.PureComponent { + mounted = false; + state: State = { loading: { measure: false, components: false, moreComponents: false } }; componentDidMount() { this.mounted = true; this.fetchMeasure(this.props); } - componentWillReceiveProps(nextProps /*: Props */) { + componentWillReceiveProps(nextProps: Props) { const { component } = this.state; const componentChanged = !component || @@ -89,7 +86,7 @@ export default class MeasureContentContainer extends React.PureComponent { this.mounted = false; } - fetchMeasure = ({ branchLike, rootComponent, fetchMeasures, metric, selected } /*: Props */) => { + fetchMeasure = ({ branchLike, rootComponent, fetchMeasures, metric, selected }: Props) => { this.updateLoading({ measure: true }); const metricKeys = [metric.key]; @@ -110,18 +107,19 @@ export default class MeasureContentContainer extends React.PureComponent { ); }; - updateLoading = (loading /*: { [string]: boolean } */) => { + updateLoading = (loading: Partial) => { if (this.mounted) { this.setState(state => ({ loading: { ...state.loading, ...loading } })); } }; - updateSelected = (component /*: string */) => + updateSelected = (component: string) => { this.props.updateQuery({ - selected: component !== this.props.rootComponent.key ? component : null + selected: component !== this.props.rootComponent.key ? component : undefined }); + }; - updateView = (view /*: string */) => this.props.updateQuery({ view }); + updateView = (view: string) => this.props.updateQuery({ view }); render() { if (!this.state.component) { @@ -136,6 +134,7 @@ export default class MeasureContentContainer extends React.PureComponent { currentUser={this.props.currentUser} leakPeriod={this.props.leakPeriod} loading={this.state.loading.measure || this.state.loading.components} + loadingMore={this.state.loading.moreComponents} measure={this.state.measure} metric={this.props.metric} metrics={this.props.metrics} diff --git a/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts b/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts index d59bb1d91e1..99c1b0e8e8c 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts +++ b/server/sonar-web/src/main/js/apps/component-measures/config/bubbles.ts @@ -46,7 +46,7 @@ export const bubbles: { }, Coverage: { x: 'complexity', y: 'coverage', size: 'uncovered_lines', yDomain: [100, 0] }, Duplications: { x: 'ncloc', y: 'duplicated_lines', size: 'duplicated_blocks' }, - // eslint-disable-next-line + // eslint-disable-next-line camelcase project_overview: { x: 'sqale_index', y: 'coverage', 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 ec49ffb6e9c..957fdc7b3e5 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 @@ -21,13 +21,10 @@ import * as React from 'react'; import ComponentsListRow from './ComponentsListRow'; import EmptyResult from './EmptyResult'; import { complementary } from '../config/complementary'; -import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n'; -import { formatMeasure, isDiffMetric, isPeriodBestValue } from '../../../helpers/measures'; +import { getLocalizedMetricName } from '../../../helpers/l10n'; import { ComponentMeasure, ComponentMeasureEnhanced, Metric, BranchLike } from '../../../app/types'; -import { Button } from '../../../components/ui/buttons'; interface Props { - bestValue?: string; branchLike?: BranchLike; components: ComponentMeasureEnhanced[]; onClick: (component: string) => void; @@ -37,93 +34,44 @@ interface Props { selectedComponent?: string; } -interface State { - hideBest: boolean; -} - -export default class ComponentsList extends React.PureComponent { - state: State = { hideBest: true }; - - componentWillReceiveProps(nextProps: Props) { - if (nextProps.metric !== this.props.metric) { - this.setState({ hideBest: true }); - } +export default function ComponentsList({ components, metric, metrics, ...props }: Props) { + if (!components.length) { + return ; } - displayAll = () => { - this.setState({ hideBest: false }); - }; - - hasBestValue = (component: ComponentMeasureEnhanced) => { - const { metric } = this.props; - const focusedMeasure = component.measures.find(measure => measure.metric.key === metric.key); - if (focusedMeasure && isDiffMetric(focusedMeasure.metric.key)) { - return isPeriodBestValue(focusedMeasure, 1); - } - return Boolean(focusedMeasure && focusedMeasure.bestValue); - }; - - renderHiddenLink = (hiddenCount: number) => { - return ( -
- {translateWithParameters( - 'component_measures.hidden_best_score_metrics', - hiddenCount, - formatMeasure(this.props.bestValue, this.props.metric.type) - )} - -
- ); - }; - - render() { - const { components, metric, metrics } = this.props; - if (!components.length) { - return ; - } - - const otherMetrics = (complementary[metric.key] || []).map(key => metrics[key]); - const notBestComponents = components.filter(component => !this.hasBestValue(component)); - const hiddenCount = components.length - notBestComponents.length; - const shouldHideBest = this.state.hideBest && hiddenCount !== components.length; - return ( - - - {otherMetrics.length > 0 && ( - - - - + {components.map(component => ( + + ))} + +
  + const otherMetrics = (complementary[metric.key] || []).map(key => metrics[key]); + return ( + + + {otherMetrics.length > 0 && ( + + + + + {otherMetrics.map(metric => ( + - {otherMetrics.map(metric => ( - - ))} - - - )} + ))} + + + )} - - {(shouldHideBest ? notBestComponents : components).map(component => ( - - ))} - -
  + {getLocalizedMetricName(metric)} + {getLocalizedMetricName(metric)} - {getLocalizedMetricName(metric)} -
- {shouldHideBest && hiddenCount > 0 && this.renderHiddenLink(hiddenCount)} -
- ); - } +
+
+ ); } 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 a4b0144ba08..ce19d48ae3f 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 @@ -22,7 +22,7 @@ import * as key from 'keymaster'; import { throttle } from 'lodash'; import ComponentsList from './ComponentsList'; import ListFooter from '../../../components/controls/ListFooter'; -import { scrollToElement } from '../../../helpers/scrolling'; +import { Button } from '../../../components/ui/buttons'; import { ComponentMeasure, ComponentMeasureEnhanced, @@ -30,14 +30,17 @@ import { Paging, BranchLike } from '../../../app/types'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { isPeriodBestValue, isDiffMetric, formatMeasure } from '../../../helpers/measures'; +import { scrollToElement } from '../../../helpers/scrolling'; interface Props { - bestValue?: string; branchLike?: BranchLike; components: ComponentMeasureEnhanced[]; fetchMore: () => void; handleSelect: (component: string) => void; handleOpen: (component: string) => void; + loadingMore: boolean; metric: Metric; metrics: { [metric: string]: Metric }; paging?: Paging; @@ -46,11 +49,16 @@ interface Props { selectedIdx?: number; } -export default class ListView extends React.PureComponent { +interface State { + showBestMeasures: boolean; +} + +export default class ListView extends React.PureComponent { listContainer?: HTMLElement | null; constructor(props: Props) { super(props); + this.state = { showBestMeasures: false }; this.selectNext = throttle(this.selectNext, 100); this.selectPrevious = throttle(this.selectPrevious, 100); } @@ -66,6 +74,9 @@ export default class ListView extends React.PureComponent { if (this.props.selectedKey !== undefined && prevProps.selectedKey !== this.props.selectedKey) { this.scrollToElement(); } + if (prevProps.metric.key !== this.props.metric.key) { + this.setState({ showBestMeasures: false }); + } } componentWillUnmount() { @@ -91,6 +102,30 @@ export default class ListView extends React.PureComponent { ['up', 'down', 'right'].forEach(action => key.unbind(action, 'measures-files')); } + getVisibleComponents = (components: ComponentMeasureEnhanced[], showBestMeasures: boolean) => { + if (showBestMeasures) { + return components; + } + const filtered = components.filter(component => !this.hasBestValue(component)); + if (filtered.length === 0) { + return components; + } + return filtered; + }; + + handleShowBestMeasures = () => { + this.setState({ showBestMeasures: true }); + }; + + hasBestValue = (component: ComponentMeasureEnhanced) => { + const { metric } = this.props; + const focusedMeasure = component.measures.find(measure => measure.metric.key === metric.key); + if (focusedMeasure && isDiffMetric(metric.key)) { + return isPeriodBestValue(focusedMeasure, 1); + } + return Boolean(focusedMeasure && focusedMeasure.bestValue); + }; + openSelected = () => { if (this.props.selectedKey !== undefined) { this.props.handleOpen(this.props.selectedKey); @@ -98,20 +133,22 @@ export default class ListView extends React.PureComponent { }; selectPrevious = () => { - const { selectedIdx } = this.props; + const { components, selectedIdx } = this.props; + const visibleComponents = this.getVisibleComponents(components, this.state.showBestMeasures); if (selectedIdx !== undefined && selectedIdx > 0) { - this.props.handleSelect(this.props.components[selectedIdx - 1].key); + this.props.handleSelect(visibleComponents[selectedIdx - 1].key); } else { - this.props.handleSelect(this.props.components[this.props.components.length - 1].key); + this.props.handleSelect(visibleComponents[visibleComponents.length - 1].key); } }; selectNext = () => { - const { selectedIdx } = this.props; - if (selectedIdx !== undefined && selectedIdx < this.props.components.length - 1) { - this.props.handleSelect(this.props.components[selectedIdx + 1].key); + const { components, selectedIdx } = this.props; + const visibleComponents = this.getVisibleComponents(components, this.state.showBestMeasures); + if (selectedIdx !== undefined && selectedIdx < visibleComponents.length - 1) { + this.props.handleSelect(visibleComponents[selectedIdx + 1].key); } else { - this.props.handleSelect(this.props.components[0].key); + this.props.handleSelect(visibleComponents[0].key); } }; @@ -125,23 +162,39 @@ export default class ListView extends React.PureComponent { }; render() { + const { components } = this.props; + const filteredComponents = this.getVisibleComponents(components, this.state.showBestMeasures); + const hidingBestMeasures = filteredComponents.length < components.length; return (
(this.listContainer = elem)}> - {this.props.paging && + {hidingBestMeasures && ( +
+ {translateWithParameters( + 'component_measures.hidden_best_score_metrics', + components.length - filteredComponents.length, + formatMeasure(this.props.metric.bestValue, this.props.metric.type) + )} + +
+ )} + {!hidingBestMeasures && + this.props.paging && this.props.components.length > 0 && ( )} diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx index 828cab940d7..61a35f44ace 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx @@ -72,13 +72,14 @@ export default class TreeMapView extends React.PureComponent { } const colorValue = colorMeasure && (isDiffMetric(metric.key) ? colorMeasure.leak : colorMeasure.value); - const sizeValue = Number( - isDiffMetric(sizeMeasure.metric.key) ? sizeMeasure.leak : sizeMeasure.value - ); - if (isNaN(sizeValue)) { + const rawSizeValue = isDiffMetric(sizeMeasure.metric.key) + ? sizeMeasure.leak + : sizeMeasure.value; + if (rawSizeValue === undefined) { return undefined; } + const sizeValue = Number(rawSizeValue); return { color: colorValue !== undefined ? (colorScale as Function)(colorValue) : theme.secondFontColor, 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 new file mode 100644 index 00000000000..38c5bccbcac --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/ComponentList-test.tsx @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +/* eslint-disable camelcase */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import ComponentsList from '../ComponentsList'; + +const COMPONENTS = [ + { + key: 'foo', + measures: [], + name: 'Foo', + organization: 'foo', + qualifier: 'TRK' + } +]; + +const METRICS = { + coverage: { id: '1', key: 'coverage', type: 'PERCENT', name: 'Coverage' }, + new_bugs: { id: '2', key: 'new_bugs', type: 'INT', name: 'New Bugs' }, + uncovered_lines: { id: '3', key: 'uncovered_lines', type: 'INT', name: 'Lines' }, + uncovered_conditions: { id: '4', key: 'uncovered_conditions', type: 'INT', name: 'Conditions' } +}; + +it('should renders correctly', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); + +it('should renders empty', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); + +it('should renders with multiple measures', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx new file mode 100644 index 00000000000..b9cce0022db --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/FilesView-test.tsx @@ -0,0 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +/* eslint-disable camelcase */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import FilesView from '../FilesView'; + +const COMPONENTS = [ + { + key: 'foo', + measures: [], + name: 'Foo', + organization: 'foo', + qualifier: 'TRK' + } +]; + +const METRICS = { coverage: { id: '1', key: 'coverage', type: 'PERCENT', name: 'Coverage' } }; + +it('should renders correctly', () => { + expect(getWrapper()).toMatchSnapshot(); +}); + +it('should render with best values hidden', () => { + expect( + getWrapper({ + components: [ + ...COMPONENTS, + { + key: 'bar', + measures: [{ bestValue: true, metric: { key: 'coverage' } }], + name: 'Bar', + organization: 'foo', + qualifier: 'TRK' + } + ] + }) + ).toMatchSnapshot(); +}); + +function getWrapper(props = {}) { + return shallow( + + ); +} 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 new file mode 100644 index 00000000000..5633f3760fe --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/ComponentList-test.tsx.snap @@ -0,0 +1,140 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should renders correctly 1`] = ` + + + + + +
+
+`; + +exports[`should renders empty 1`] = ``; + +exports[`should renders with multiple measures 1`] = ` + + + + + + + + + + + + + +
+   + + + Coverage + + + + Lines + + + + Conditions + +
+
+`; 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 new file mode 100644 index 00000000000..b63601b1b71 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/__tests__/__snapshots__/FilesView-test.tsx.snap @@ -0,0 +1,110 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render with best values hidden 1`] = ` +
+ +
+ component_measures.hidden_best_score_metrics.1. + +
+
+`; + +exports[`should renders correctly 1`] = ` +
+ + +
+`; 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 553edee41f4..a02181c7e95 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 @@ -21,10 +21,14 @@ import { groupBy, memoize, sortBy, toPairs } from 'lodash'; import { domains } from './config/domains'; import { bubbles } from './config/bubbles'; import { getLocalizedMetricName } from '../../helpers/l10n'; -import { ComponentMeasure, ComponentMeasureEnhanced, Metric } from '../../app/types'; +import { + ComponentMeasure, + ComponentMeasureEnhanced, + Metric, + MeasureEnhanced +} from '../../app/types'; import { enhanceMeasure } from '../../components/measure/utils'; import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query'; -import { MeasureEnhanced } from '../../helpers/measures'; export const PROJECT_OVERVEW = 'project_overview'; export const DEFAULT_VIEW = 'list'; diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx index dcf365a6615..63aed5899de 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx @@ -31,7 +31,7 @@ import throwGlobalError from '../../../app/utils/throwGlobalError'; import { getMeasuresAndMeta } from '../../../api/measures'; import { getAllTimeMachineData, History } from '../../../api/time-machine'; import { parseDate } from '../../../helpers/dates'; -import { enhanceMeasuresWithMetrics, MeasureEnhanced } from '../../../helpers/measures'; +import { enhanceMeasuresWithMetrics } from '../../../helpers/measures'; import { getLeakPeriod, Period } from '../../../helpers/periods'; import { get } from '../../../helpers/storage'; import { METRICS, HISTORY_METRICS_LIST } from '../utils'; @@ -48,7 +48,7 @@ import { } from '../../../helpers/branches'; import { fetchMetrics } from '../../../store/rootActions'; import { getMetrics } from '../../../store/rootReducer'; -import { BranchLike, Component, Metric } from '../../../app/types'; +import { BranchLike, Component, Metric, MeasureEnhanced } from '../../../app/types'; import { translate } from '../../../helpers/l10n'; import '../styles.css'; diff --git a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx index 2ff27976bf8..b5509cbd738 100644 --- a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx +++ b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx @@ -30,8 +30,7 @@ import { isDiffMetric, getPeriodValue, getShortType, - getRatingTooltip, - MeasureEnhanced + getRatingTooltip } from '../../../helpers/measures'; import { getLocalizedMetricName } from '../../../helpers/l10n'; import { getPeriodDate } from '../../../helpers/periods'; @@ -40,7 +39,7 @@ import { getComponentIssuesUrl, getMeasureHistoryUrl } from '../../../helpers/urls'; -import { Component, BranchLike } from '../../../app/types'; +import { Component, BranchLike, MeasureEnhanced } from '../../../app/types'; import { History } from '../../../api/time-machine'; import { getBranchLikeQuery } from '../../../helpers/branches'; diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx index 64a0a9e4ea6..cd93c3a9fbd 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx @@ -35,11 +35,11 @@ import { Metric, BranchLike, CurrentUser, - Organization + Organization, + MeasureEnhanced } from '../../../app/types'; import { History } from '../../../api/time-machine'; import { translate } from '../../../helpers/l10n'; -import { MeasureEnhanced } from '../../../helpers/measures'; import { hasPrivateAccess } from '../../../helpers/organizations'; import { getCurrentUser, diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx index f1b25609103..4fdc6e23ebf 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx @@ -22,10 +22,10 @@ import * as classNames from 'classnames'; import DrilldownLink from '../../../components/shared/DrilldownLink'; import LanguageDistributionContainer from '../../../components/charts/LanguageDistributionContainer'; import SizeRating from '../../../components/ui/SizeRating'; -import { formatMeasure, MeasureEnhanced } from '../../../helpers/measures'; +import { formatMeasure } from '../../../helpers/measures'; import { getMetricName } from '../helpers/metrics'; import { translate } from '../../../helpers/l10n'; -import { LightComponent, BranchLike } from '../../../app/types'; +import { LightComponent, BranchLike, MeasureEnhanced } from '../../../app/types'; interface Props { branchLike?: BranchLike; diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx index dc71ec4d2aa..cf25dc909b5 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/MeasuresOverlay.tsx @@ -26,7 +26,7 @@ import { Button } from '../../ui/buttons'; import { getFacets } from '../../../api/issues'; import { getMeasures } from '../../../api/measures'; import { getAllMetrics } from '../../../api/metrics'; -import { FacetValue, SourceViewerFile, BranchLike } from '../../../app/types'; +import { FacetValue, SourceViewerFile, BranchLike, MeasureEnhanced } from '../../../app/types'; import Modal from '../../controls/Modal'; import Measure from '../../measure/Measure'; import QualifierIcon from '../../icons-components/QualifierIcon'; @@ -38,7 +38,6 @@ import { SEVERITIES, TYPES } from '../../../helpers/constants'; import { translate, getLocalizedMetricName } from '../../../helpers/l10n'; import { formatMeasure, - MeasureEnhanced, getDisplayMetrics, enhanceMeasuresWithMetrics } from '../../../helpers/measures'; diff --git a/server/sonar-web/src/main/js/components/measure/utils.ts b/server/sonar-web/src/main/js/components/measure/utils.ts index 626c65f552f..e45496fbd1a 100644 --- a/server/sonar-web/src/main/js/components/measure/utils.ts +++ b/server/sonar-web/src/main/js/components/measure/utils.ts @@ -17,13 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { - getRatingTooltip as nextGetRatingTooltip, - isDiffMetric, - Measure, - MeasureEnhanced -} from '../../helpers/measures'; -import { Metric } from '../../app/types'; +import { getRatingTooltip as nextGetRatingTooltip, isDiffMetric } from '../../helpers/measures'; +import { Metric, Measure, MeasureEnhanced } from '../../app/types'; const KNOWN_RATINGS = ['sqale_rating', 'reliability_rating', 'security_rating']; diff --git a/server/sonar-web/src/main/js/helpers/measures.ts b/server/sonar-web/src/main/js/helpers/measures.ts index 171e04121f2..78256f28078 100644 --- a/server/sonar-web/src/main/js/helpers/measures.ts +++ b/server/sonar-web/src/main/js/helpers/measures.ts @@ -18,31 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { translate, translateWithParameters, getCurrentLocale } from './l10n'; -import { Metric } from '../app/types'; +import { Metric, Measure, MeasureEnhanced } from '../app/types'; const HOURS_IN_DAY = 8; -export interface MeasurePeriod { - bestValue?: boolean; - index: number; - value: string; -} - -export interface MeasureIntern { - bestValue?: boolean; - periods?: MeasurePeriod[]; - value?: string; -} - -export interface Measure extends MeasureIntern { - metric: string; -} - -export interface MeasureEnhanced extends MeasureIntern { - metric: Metric; - leak?: string; -} - interface Formatter { (value: string | number, options?: any): string; } -- 2.39.5