From 11dea08f3d132f6e580acc8a60b44f6ed9f124ae Mon Sep 17 00:00:00 2001 From: David Cho-Lerat Date: Wed, 6 Dec 2023 12:22:51 +0100 Subject: [PATCH] SONAR-21200 Activity page: don't show data from default branch on refresh --- .../main/js/apps/code/components/CodeApp.tsx | 8 +---- .../components/ComponentMeasuresApp.tsx | 6 ++-- .../components/ProjectActivityApp.tsx | 33 ++++++++++++++----- .../__tests__/ProjectActivityApp-it.tsx | 29 +++++++++++++--- .../sonar-web/src/main/js/queries/branch.tsx | 12 ++++--- 5 files changed, 59 insertions(+), 29 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx index 0e5cdc76e7b..3a3b248a629 100644 --- a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx @@ -22,8 +22,7 @@ import withComponentContext from '../../../app/components/componentContext/withC import withMetricsContext from '../../../app/components/metrics/withMetricsContext'; import { Location, Router, withRouter } from '../../../components/hoc/withRouter'; import { CodeScope, getCodeUrl, getProjectUrl } from '../../../helpers/urls'; -import { useBranchesQuery } from '../../../queries/branch'; -import { BranchLike } from '../../../types/branch-like'; +import { WithBranchLikesProps, useBranchesQuery } from '../../../queries/branch'; import { ComponentQualifier, isPortfolioLike } from '../../../types/component'; import { Breadcrumb, Component, ComponentMeasure, Dict, Metric } from '../../../types/types'; import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket'; @@ -225,11 +224,6 @@ class CodeApp extends React.Component { } } -interface WithBranchLikesProps { - branchLikes?: BranchLike[]; - branchLike?: BranchLike; -} - function withBranchLikes

( WrappedComponent: React.ComponentType>, ): React.ComponentType>> { diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx index e07f41aa4fc..9189b69a978 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx @@ -40,8 +40,7 @@ import { enhanceMeasure } from '../../../components/measure/utils'; import '../../../components/search-navigator.css'; import { getBranchLikeQuery, isPullRequest, isSameBranchLike } from '../../../helpers/branch-like'; import { translate } from '../../../helpers/l10n'; -import { useBranchesQuery } from '../../../queries/branch'; -import { BranchLike } from '../../../types/branch-like'; +import { WithBranchLikesProps, useBranchesQuery } from '../../../queries/branch'; import { ComponentQualifier, isPortfolioLike } from '../../../types/component'; import { MeasurePageView } from '../../../types/measures'; import { MetricKey } from '../../../types/metrics'; @@ -65,8 +64,7 @@ import MeasureContent from './MeasureContent'; import MeasureOverviewContainer from './MeasureOverviewContainer'; import MeasuresEmpty from './MeasuresEmpty'; -interface Props { - branchLike?: BranchLike; +interface Props extends WithBranchLikesProps { component: ComponentMeasure; location: Location; router: Router; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx index c9a199e4b27..cabdb5cbfc3 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx @@ -38,11 +38,10 @@ import { isCustomGraph, } from '../../../components/activity-graph/utils'; import { Location, Router, withRouter } from '../../../components/hoc/withRouter'; -import { getBranchLikeQuery } from '../../../helpers/branch-like'; +import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like'; import { parseDate } from '../../../helpers/dates'; import { serializeStringArray } from '../../../helpers/query'; -import { withBranchLikes } from '../../../queries/branch'; -import { BranchLike } from '../../../types/branch-like'; +import { WithBranchLikesProps, withBranchLikes } from '../../../queries/branch'; import { ComponentQualifier, isApplication, @@ -67,8 +66,7 @@ import { } from '../utils'; import ProjectActivityAppRenderer from './ProjectActivityAppRenderer'; -interface Props { - branchLike?: BranchLike; +interface Props extends WithBranchLikesProps { component: Component; location: Location; metrics: Dict; @@ -108,13 +106,26 @@ class ProjectActivityApp extends React.PureComponent { componentDidMount() { this.mounted = true; - this.firstLoadData(this.state.query, this.props.component); + if (this.isBranchReady()) { + this.firstLoadData(this.state.query, this.props.component); + } } componentDidUpdate(prevProps: Props) { - if (prevProps.location.query !== this.props.location.query) { - const query = parseQuery(this.props.location.query); - if (query.graph !== this.state.query.graph || customMetricsChanged(this.state.query, query)) { + const unparsedQuery = this.props.location.query; + + const hasQueryChanged = prevProps.location.query !== unparsedQuery; + + const hasBranchChanged = !isSameBranchLike(prevProps.branchLike, this.props.branchLike); + + if (this.isBranchReady() && (hasBranchChanged || hasQueryChanged)) { + const query = parseQuery(unparsedQuery); + + if ( + query.graph !== this.state.query.graph || + customMetricsChanged(this.state.query, query) || + hasBranchChanged + ) { if (this.state.initialized) { this.updateGraphData(query.graph || DEFAULT_GRAPH, query.customMetrics); } else { @@ -129,6 +140,10 @@ class ProjectActivityApp extends React.PureComponent { this.mounted = false; } + isBranchReady = () => + isPortfolioLike(this.props.component.qualifier) || + (this.props.branchLike !== undefined && !this.props.isFetchingBranch); + handleAddCustomEvent = (analysisKey: string, name: string, category?: string) => { return createEvent(analysisKey, name, category).then(({ analysis, ...event }) => { if (this.mounted) { diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx index ec5784f739d..2119741f468 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx @@ -27,6 +27,7 @@ import { Route } from 'react-router-dom'; import ApplicationServiceMock from '../../../../api/mocks/ApplicationServiceMock'; import { ProjectActivityServiceMock } from '../../../../api/mocks/ProjectActivityServiceMock'; import { TimeMachineServiceMock } from '../../../../api/mocks/TimeMachineServiceMock'; +import { mockBranchList } from '../../../../api/mocks/data/branches'; import { parseDate } from '../../../../helpers/dates'; import { mockComponent } from '../../../../helpers/mocks/component'; import { @@ -57,11 +58,22 @@ jest.mock('../../../../helpers/storage', () => ({ save: jest.fn(), })); +jest.mock('../../../../api/branches', () => ({ + getBranches: () => { + isBranchReady = true; + return Promise.resolve(mockBranchList()); + }, +})); + const applicationHandler = new ApplicationServiceMock(); const projectActivityHandler = new ProjectActivityServiceMock(); const timeMachineHandler = new TimeMachineServiceMock(); +let isBranchReady = false; + beforeEach(() => { + isBranchReady = false; + jest.clearAllMocks(); applicationHandler.reset(); projectActivityHandler.reset(); @@ -140,7 +152,8 @@ describe('rendering', () => { breadcrumbs: [{ key: 'breadcrumb', name: 'breadcrumb', qualifier }], }), ); - await ui.appLoaded(); + + await ui.appLoaded({ doNotWaitForBranch: true }); expect(ui.newCodeLegend.query()).not.toBeInTheDocument(); }, @@ -367,7 +380,7 @@ describe('data loading', () => { }), ); - await ui.appLoaded(); + await ui.appLoaded({ doNotWaitForBranch: true }); // If it didn't fail, it means we correctly queried for project "foo". expect(ui.activityItem.getAll().length).toBe(4); @@ -546,7 +559,7 @@ function getPageObject() { deleteBtn: byRole('button', { name: 'delete' }), // Misc. - loading: byLabelText('loading'), + loading: byText('loading'), baseline: byText('project_activity.new_code_period_start'), bugsPopupCell: byRole('cell', { name: MetricKey.bugs }), monthSelector: byTestId('month-select'), @@ -557,10 +570,16 @@ function getPageObject() { user, ui: { ...ui, - async appLoaded() { + async appLoaded({ doNotWaitForBranch }: { doNotWaitForBranch?: boolean } = {}) { await waitFor(() => { expect(ui.loading.query()).not.toBeInTheDocument(); }); + + if (!doNotWaitForBranch) { + await waitFor(() => { + expect(isBranchReady).toBe(true); + }); + } }, async changeGraphType(type: GraphType) { @@ -690,7 +709,7 @@ function renderProjectActivityAppContainer( }), ) { return renderAppWithComponentContext( - 'project/activity', + `project/activity?id=${component.key}`, () => } />, { metrics: keyBy( diff --git a/server/sonar-web/src/main/js/queries/branch.tsx b/server/sonar-web/src/main/js/queries/branch.tsx index 78bba188984..7bcba936c95 100644 --- a/server/sonar-web/src/main/js/queries/branch.tsx +++ b/server/sonar-web/src/main/js/queries/branch.tsx @@ -326,11 +326,15 @@ export function useRefreshBranches() { }; } +export interface WithBranchLikesProps { + branchLikes?: BranchLike[]; + branchLike?: BranchLike; + isFetchingBranch?: boolean; +} + export function withBranchLikes

( - WrappedComponent: React.ComponentType< - P & { branchLikes?: BranchLike[]; branchLike?: BranchLike; isFetchingBranch?: boolean } - >, -): React.ComponentType> { + WrappedComponent: React.ComponentType>, +): React.ComponentType>> { return function WithBranchLike(p: P) { const { data, isFetching } = useBranchesQuery(p.component); return ( -- 2.39.5