]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21200 Activity page: don't show data from default branch on refresh
authorDavid Cho-Lerat <david.cho-lerat@sonarsource.com>
Wed, 6 Dec 2023 11:22:51 +0000 (12:22 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 12 Dec 2023 20:02:44 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx
server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx
server/sonar-web/src/main/js/queries/branch.tsx

index 0e5cdc76e7bf7f5e110a2faf705090bd0b86af70..3a3b248a629e1c28ce767b48aab1130af66c5414 100644 (file)
@@ -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<Props, State> {
   }
 }
 
-interface WithBranchLikesProps {
-  branchLikes?: BranchLike[];
-  branchLike?: BranchLike;
-}
-
 function withBranchLikes<P extends { component?: Component }>(
   WrappedComponent: React.ComponentType<React.PropsWithChildren<P & WithBranchLikesProps>>,
 ): React.ComponentType<React.PropsWithChildren<Omit<P, 'branchLike' | 'branchLikes'>>> {
index e07f41aa4fc17b1bb32a684c358697f53e88d842..9189b69a978e1f7908500033282c056223006bf8 100644 (file)
@@ -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;
index c9a199e4b27995705c4d711dac8da0237764eeec..cabdb5cbfc334bebcb12fdaa77ee408504e14a79 100644 (file)
@@ -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<Metric>;
@@ -108,13 +106,26 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
   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<Props, State> {
     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) {
index ec5784f739d8eb213698822d87cca0421cec51ef..2119741f4680f0c3bf9fbd1bd07ae8643e004ee4 100644 (file)
@@ -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}`,
     () => <Route path="*" element={<ProjectActivityAppContainer />} />,
     {
       metrics: keyBy(
index 78bba18898455ec900c334a0f6868c7bb95aec3c..7bcba936c9547cebe3631165222038701e15ef83 100644 (file)
@@ -326,11 +326,15 @@ export function useRefreshBranches() {
   };
 }
 
+export interface WithBranchLikesProps {
+  branchLikes?: BranchLike[];
+  branchLike?: BranchLike;
+  isFetchingBranch?: boolean;
+}
+
 export function withBranchLikes<P extends { component?: Component }>(
-  WrappedComponent: React.ComponentType<
-    P & { branchLikes?: BranchLike[]; branchLike?: BranchLike; isFetchingBranch?: boolean }
-  >,
-): React.ComponentType<Omit<P, 'branchLike' | 'branchLikes'>> {
+  WrappedComponent: React.ComponentType<React.PropsWithChildren<P & WithBranchLikesProps>>,
+): React.ComponentType<React.PropsWithChildren<Omit<P, 'branchLike' | 'branchLikes'>>> {
   return function WithBranchLike(p: P) {
     const { data, isFetching } = useBranchesQuery(p.component);
     return (