]> source.dussan.org Git - sonarqube.git/commitdiff
[NO JIRA] Cleanup project activity types, mocks, and components
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Tue, 11 Oct 2022 10:23:02 +0000 (12:23 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 13 Oct 2022 20:03:19 +0000 (20:03 +0000)
67 files changed:
server/sonar-web/src/main/js/api/projectActivity.ts
server/sonar-web/src/main/js/apps/overview/branches/ActivityPanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/Analysis.tsx
server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx
server/sonar-web/src/main/js/apps/overview/branches/Event.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-test.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/Analysis-test.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-test.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/Event-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/actions.ts
server/sonar-web/src/main/js/apps/projectActivity/components/DefinitionChangeEventInner.tsx [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/Event.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.tsx [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/Events.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppRenderer.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/RichQualityGateEventInner.tsx [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/DefinitionChangeEventInner-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/Event-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/EventInner-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/Events-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysis-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/RichQualityGateEventInner-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/DefinitionChangeEventInner-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/EventInner-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/forms/__tests__/RemoveEventForm-test.tsx
server/sonar-web/src/main/js/apps/projectActivity/utils.ts
server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisList.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/BranchAnalysisList-test.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/BranchAnalysisListRenderer-test.tsx
server/sonar-web/src/main/js/components/activity-graph/DataTableModal.tsx
server/sonar-web/src/main/js/components/activity-graph/DefinitionChangeEventInner.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltips.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltipsContentEvents.tsx
server/sonar-web/src/main/js/components/activity-graph/RichQualityGateEventInner.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/DefinitionChangeEventInner-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/EventInner-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/GraphHistory-test.tsx
server/sonar-web/src/main/js/components/activity-graph/__tests__/RichQualityGateEventInner-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/DefinitionChangeEventInner-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/EventInner-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/GraphHistory-test.tsx.snap
server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/activity-graph/utils.ts
server/sonar-web/src/main/js/helpers/mocks/project-activity.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/testMocks.ts
server/sonar-web/src/main/js/types/project-activity.ts
server/sonar-web/src/main/js/types/types.ts

index 9fde41ead19951f3765beee175a4d670c90dd216..06e8e582e43005c8d0af36982f4bca0c72d21cc0 100644 (file)
@@ -20,7 +20,8 @@
 import { throwGlobalError } from '../helpers/error';
 import { getJSON, post, postJSON, RequestData } from '../helpers/request';
 import { BranchParameters } from '../types/branch-like';
-import { Analysis, Paging } from '../types/types';
+import { Analysis } from '../types/project-activity';
+import { Paging } from '../types/types';
 
 export enum ProjectActivityStatuses {
   STATUS_PROCESSED = 'P',
index c894d53d81976d2a31fb3482f4ed3cda9afc95d0..8fb7721b7d6476662811ce204f7b13bccb3e4170 100644 (file)
@@ -32,8 +32,12 @@ import { parseDate } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { localizeMetric } from '../../../helpers/measures';
 import { BranchLike } from '../../../types/branch-like';
-import { GraphType, MeasureHistory } from '../../../types/project-activity';
-import { Analysis as AnalysisType, Component, Metric } from '../../../types/types';
+import {
+  Analysis as AnalysisType,
+  GraphType,
+  MeasureHistory
+} from '../../../types/project-activity';
+import { Component, Metric } from '../../../types/types';
 import Analysis from './Analysis';
 
 export interface ActivityPanelProps {
index 945256ccac559a973b9b4e39ec90f7165c876df7..6a8f47d900122674c7ae5ed9c68f5e014f8db978 100644 (file)
@@ -22,7 +22,7 @@ import * as React from 'react';
 import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
 import { translate } from '../../../helpers/l10n';
 import { ComponentQualifier } from '../../../types/component';
-import { Analysis as TypeAnalysis } from '../../../types/types';
+import { Analysis as TypeAnalysis } from '../../../types/project-activity';
 import Event from './Event';
 
 export interface AnalysisProps {
index 706194c36cea4c324a1cf56c771f286b9ff235e7..cafc0f47fc18ee9dd994eb4d2eede7040106dffe 100644 (file)
@@ -47,9 +47,9 @@ import { ApplicationPeriod } from '../../../types/application';
 import { Branch, BranchLike } from '../../../types/branch-like';
 import { ComponentQualifier } from '../../../types/component';
 import { MetricKey } from '../../../types/metrics';
-import { GraphType, MeasureHistory } from '../../../types/project-activity';
+import { Analysis, GraphType, MeasureHistory } from '../../../types/project-activity';
 import { QualityGateStatus, QualityGateStatusCondition } from '../../../types/quality-gates';
-import { Analysis, Component, MeasureEnhanced, Metric, Period } from '../../../types/types';
+import { Component, MeasureEnhanced, Metric, Period } from '../../../types/types';
 import '../styles.css';
 import { HISTORY_METRICS_LIST, METRICS } from '../utils';
 import BranchOverviewRenderer from './BranchOverviewRenderer';
index c718f5b46583d60655503a9d9947f75295d9ec39..969790a0bfb07f53319a8ec4674e893527157d03 100644 (file)
@@ -24,9 +24,9 @@ import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
 import { ApplicationPeriod } from '../../../types/application';
 import { Branch } from '../../../types/branch-like';
 import { ComponentQualifier } from '../../../types/component';
-import { GraphType, MeasureHistory } from '../../../types/project-activity';
+import { Analysis, GraphType, MeasureHistory } from '../../../types/project-activity';
 import { QualityGateStatus } from '../../../types/quality-gates';
-import { Analysis, Component, MeasureEnhanced, Metric, Period } from '../../../types/types';
+import { Component, MeasureEnhanced, Metric, Period } from '../../../types/types';
 import ActivityPanel from './ActivityPanel';
 import FirstAnalysisNextStepsNotif from './FirstAnalysisNextStepsNotif';
 import MeasuresPanel from './MeasuresPanel';
index 3b01c1f19dd149a0f9d53df2d758c760a00777df..b10845593df45c36271396f6e955273e83661887 100644 (file)
  */
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
+import { isDefinitionChangeEvent } from '../../../components/activity-graph/DefinitionChangeEventInner';
+import { isRichQualityGateEvent } from '../../../components/activity-graph/RichQualityGateEventInner';
 import Level from '../../../components/ui/Level';
 import { translate } from '../../../helpers/l10n';
-import { AnalysisEvent } from '../../../types/types';
-import { isDefinitionChangeEvent } from '../../projectActivity/components/DefinitionChangeEventInner';
-import { isRichQualityGateEvent } from '../../projectActivity/components/RichQualityGateEventInner';
+import { AnalysisEvent } from '../../../types/project-activity';
 
 interface Props {
   event: AnalysisEvent;
index 65ebd44f5749ede3897e7721ca4ae972d4b8d8a2..93f43fa57055300c18341ffee9e0f960597eea5b 100644 (file)
@@ -23,12 +23,8 @@ import GraphsHistory from '../../../../components/activity-graph/GraphsHistory';
 import { parseDate } from '../../../../helpers/dates';
 import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
 import { mockComponent } from '../../../../helpers/mocks/component';
-import {
-  mockAnalysis,
-  mockAnalysisEvent,
-  mockMeasure,
-  mockMetric
-} from '../../../../helpers/testMocks';
+import { mockAnalysis, mockAnalysisEvent } from '../../../../helpers/mocks/project-activity';
+import { mockMeasure, mockMetric } from '../../../../helpers/testMocks';
 import { GraphType } from '../../../../types/project-activity';
 import { ActivityPanel, ActivityPanelProps } from '../ActivityPanel';
 
index 75734f2c67098f3c3bc29aee716726c673ea5ed4..d5d1e9fd151cadf77d9fcd394ce9211f4ec1a2b3 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockAnalysis } from '../../../../helpers/testMocks';
+import { mockAnalysis } from '../../../../helpers/mocks/project-activity';
 import { ComponentQualifier } from '../../../../types/component';
 import { Analysis, AnalysisProps } from '../Analysis';
 
index 1ce696193a03319baed255742dc03421bc18c083..11d5e0b905474d3eaa4386454dd6201d3f57ae92 100644 (file)
@@ -31,7 +31,7 @@ import { getActivityGraph, saveActivityGraph } from '../../../../components/acti
 import { isDiffMetric } from '../../../../helpers/measures';
 import { mockBranch, mockMainBranch } from '../../../../helpers/mocks/branch-like';
 import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockAnalysis } from '../../../../helpers/testMocks';
+import { mockAnalysis } from '../../../../helpers/mocks/project-activity';
 import { waitAndUpdate } from '../../../../helpers/testUtils';
 import { ComponentQualifier } from '../../../../types/component';
 import { MetricKey } from '../../../../types/metrics';
@@ -144,7 +144,7 @@ jest.mock('../../../../api/time-machine', () => {
 });
 
 jest.mock('../../../../api/projectActivity', () => {
-  const { mockAnalysis } = jest.requireActual('../../../../helpers/testMocks');
+  const { mockAnalysis } = jest.requireActual('../../../../helpers/mocks/project-activity');
   return {
     getProjectActivity: jest.fn().mockResolvedValue({
       analyses: [
index eeab19b971ca0db15082a41abf21babbae4e950e..9426d128179b544b8a817ae990fac054ef6a09e6 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { AnalysisEvent } from '../../../../types/types';
+import { AnalysisEvent } from '../../../../types/project-activity';
 import { Event } from '../Event';
 
 it('should render an event correctly', () => {
index df14281e68372a8698d34ae47248d1a15286d336..52b46181dfc402a6ab386649b0a1148e142048a3 100644 (file)
@@ -17,7 +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 { AnalysisEvent } from '../../types/types';
+import { AnalysisEvent } from '../../types/project-activity';
 import { State } from './components/ProjectActivityApp';
 
 export function addCustomEvent(analysis: string, event: AnalysisEvent) {
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/DefinitionChangeEventInner.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/DefinitionChangeEventInner.tsx
deleted file mode 100644 (file)
index 41c393a..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import Link from '../../../components/common/Link';
-import { ButtonLink } from '../../../components/controls/buttons';
-import BranchIcon from '../../../components/icons/BranchIcon';
-import DropdownIcon from '../../../components/icons/DropdownIcon';
-import { isMainBranch } from '../../../helpers/branch-like';
-import { translate } from '../../../helpers/l10n';
-import { limitComponentName } from '../../../helpers/path';
-import { getProjectUrl } from '../../../helpers/urls';
-import { BranchLike } from '../../../types/branch-like';
-import { AnalysisEvent } from '../../../types/types';
-
-export type DefinitionChangeEvent = AnalysisEvent &
-  Required<Pick<AnalysisEvent, 'definitionChange'>>;
-
-export function isDefinitionChangeEvent(event: AnalysisEvent): event is DefinitionChangeEvent {
-  return event.category === 'DEFINITION_CHANGE' && event.definitionChange !== undefined;
-}
-
-interface Props {
-  branchLike: BranchLike | undefined;
-  event: DefinitionChangeEvent;
-  readonly?: boolean;
-}
-
-interface State {
-  expanded: boolean;
-}
-
-export class DefinitionChangeEventInner extends React.PureComponent<Props, State> {
-  state: State = { expanded: false };
-
-  stopPropagation = (event: React.MouseEvent<HTMLAnchorElement>) => {
-    event.stopPropagation();
-  };
-
-  toggleProjectsList = () => {
-    this.setState(state => ({ expanded: !state.expanded }));
-  };
-
-  renderProjectLink = (project: { key: string; name: string }, branch: string | undefined) => (
-    <Link
-      onClick={this.stopPropagation}
-      title={project.name}
-      to={getProjectUrl(project.key, branch)}>
-      {limitComponentName(project.name, 28)}
-    </Link>
-  );
-
-  renderBranch = (branch = translate('branches.main_branch')) => (
-    <span className="nowrap" title={branch}>
-      <BranchIcon className="little-spacer-left text-text-top" />
-      {branch}
-    </span>
-  );
-
-  renderProjectChange(project: {
-    changeType: string;
-    key: string;
-    name: string;
-    branch?: string;
-    newBranch?: string;
-    oldBranch?: string;
-  }) {
-    const mainBranch = !this.props.branchLike || isMainBranch(this.props.branchLike);
-
-    if (project.changeType === 'ADDED') {
-      const message = mainBranch
-        ? 'event.definition_change.added'
-        : 'event.definition_change.branch_added';
-      return (
-        <div className="text-ellipsis">
-          <FormattedMessage
-            defaultMessage={translate(message)}
-            id={message}
-            values={{
-              project: this.renderProjectLink(project, project.branch),
-              branch: this.renderBranch(project.branch)
-            }}
-          />
-        </div>
-      );
-    } else if (project.changeType === 'REMOVED') {
-      const message = mainBranch
-        ? 'event.definition_change.removed'
-        : 'event.definition_change.branch_removed';
-      return (
-        <div className="text-ellipsis">
-          <FormattedMessage
-            defaultMessage={translate(message)}
-            id={message}
-            values={{
-              project: this.renderProjectLink(project, project.branch),
-              branch: this.renderBranch(project.branch)
-            }}
-          />
-        </div>
-      );
-    } else if (project.changeType === 'BRANCH_CHANGED') {
-      return (
-        <FormattedMessage
-          defaultMessage={translate('event.definition_change.branch_replaced')}
-          id="event.definition_change.branch_replaced"
-          values={{
-            project: this.renderProjectLink(project, project.newBranch),
-            oldBranch: this.renderBranch(project.oldBranch),
-            newBranch: this.renderBranch(project.newBranch)
-          }}
-        />
-      );
-    }
-    return null;
-  }
-
-  render() {
-    const { event, readonly } = this.props;
-    const { expanded } = this.state;
-    return (
-      <>
-        <span className="note">
-          {translate('event.category', event.category)}
-          {!readonly && ':'}
-        </span>
-
-        {!readonly && (
-          <div>
-            <ButtonLink
-              className="project-activity-event-inner-more-link"
-              onClick={this.toggleProjectsList}
-              stopPropagation={true}>
-              {expanded ? translate('hide') : translate('more')}
-              <DropdownIcon className="little-spacer-left" turned={expanded} />
-            </ButtonLink>
-          </div>
-        )}
-
-        {expanded && (
-          <ul className="spacer-left spacer-top">
-            {event.definitionChange.projects.map(project => (
-              <li className="display-flex-center spacer-top" key={project.key}>
-                {this.renderProjectChange(project)}
-              </li>
-            ))}
-          </ul>
-        )}
-      </>
-    );
-  }
-}
index 9e984bbc47de4bedcae2d57334673f533c7c5456..0b9a7ec252fd1602ed9b682944a460803ae63a46 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import EventInner from '../../../components/activity-graph/EventInner';
 import { DeleteButton, EditButton } from '../../../components/controls/buttons';
 import { translate } from '../../../helpers/l10n';
-import { AnalysisEvent } from '../../../types/types';
-import EventInner from './EventInner';
+import { AnalysisEvent } from '../../../types/project-activity';
 import ChangeEventForm from './forms/ChangeEventForm';
 import RemoveEventForm from './forms/RemoveEventForm';
 
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.tsx
deleted file mode 100644 (file)
index 8b636c6..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { ComponentContext } from '../../../app/components/componentContext/ComponentContext';
-import Tooltip from '../../../components/controls/Tooltip';
-import { translate } from '../../../helpers/l10n';
-import { AnalysisEvent } from '../../../types/types';
-import { DefinitionChangeEventInner, isDefinitionChangeEvent } from './DefinitionChangeEventInner';
-import { isRichQualityGateEvent, RichQualityGateEventInner } from './RichQualityGateEventInner';
-
-export interface EventInnerProps {
-  event: AnalysisEvent;
-  readonly?: boolean;
-}
-
-export default function EventInner({ event, readonly }: EventInnerProps) {
-  if (isRichQualityGateEvent(event)) {
-    return <RichQualityGateEventInner event={event} readonly={readonly} />;
-  } else if (isDefinitionChangeEvent(event)) {
-    return (
-      <ComponentContext.Consumer>
-        {({ branchLike }) => (
-          <DefinitionChangeEventInner branchLike={branchLike} event={event} readonly={readonly} />
-        )}
-      </ComponentContext.Consumer>
-    );
-  }
-
-  return (
-    <Tooltip overlay={event.description || null}>
-      <span className="text-middle">
-        <span className="note little-spacer-right">
-          {translate('event.category', event.category)}:
-        </span>
-        <strong className="spacer-right">{event.name}</strong>
-      </span>
-    </Tooltip>
-  );
-}
index d201d7366fec85bcd90dcb86bcef3c3dc4ee47e4..df33ccca6037ef163fa6772a79178691470caa41 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { sortBy } from 'lodash';
 import * as React from 'react';
-import { AnalysisEvent } from '../../../types/types';
+import { AnalysisEvent } from '../../../types/project-activity';
 import Event from './Event';
 
 export interface EventsProps {
index 3a6966eecc3e8205e272fe7f352321fadff669ac..3e8b7b2ba4adf243a273df4814859e9c32723a65 100644 (file)
@@ -26,7 +26,7 @@ import DateFormatter from '../../../components/intl/DateFormatter';
 import { toShortNotSoISOString } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 import { ComponentQualifier } from '../../../types/component';
-import { ParsedAnalysis } from '../../../types/types';
+import { ParsedAnalysis } from '../../../types/project-activity';
 import { activityQueryChanged, getAnalysesByVersionByDay, Query } from '../utils';
 import ProjectActivityAnalysis from './ProjectActivityAnalysis';
 
index 3988b78c7b6f5378d4c74710677b6939958444fe..a38c2459ba2f820435b241433299e70136913941 100644 (file)
@@ -30,7 +30,7 @@ import { PopupPlacement } from '../../../components/ui/popups';
 import { parseDate } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { scrollToElement } from '../../../helpers/scrolling';
-import { ParsedAnalysis } from '../../../types/types';
+import { ParsedAnalysis } from '../../../types/project-activity';
 import Events from './Events';
 import AddEventForm from './forms/AddEventForm';
 import RemoveAnalysisForm from './forms/RemoveAnalysisForm';
index 46099ce9b07e61a723c2ed506ece651753ead152..59bb31c2d52f33566a3c1218592363d74114b64d 100644 (file)
@@ -42,8 +42,8 @@ import { parseDate } from '../../../helpers/dates';
 import { serializeStringArray } from '../../../helpers/query';
 import { BranchLike } from '../../../types/branch-like';
 import { MetricKey } from '../../../types/metrics';
-import { GraphType, MeasureHistory } from '../../../types/project-activity';
-import { Component, Metric, Paging, ParsedAnalysis, RawQuery } from '../../../types/types';
+import { GraphType, MeasureHistory, ParsedAnalysis } from '../../../types/project-activity';
+import { Component, Metric, Paging, RawQuery } from '../../../types/types';
 import * as actions from '../actions';
 import {
   customMetricsChanged,
index f7dc79249f30f6325d17b6f1ab86f31d3f7e0e6e..9a7108ad441866d201d302725ebf6cd094575058 100644 (file)
@@ -23,8 +23,8 @@ import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget';
 import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import { parseDate } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
-import { MeasureHistory } from '../../../types/project-activity';
-import { Component, Metric, ParsedAnalysis } from '../../../types/types';
+import { MeasureHistory, ParsedAnalysis } from '../../../types/project-activity';
+import { Component, Metric } from '../../../types/types';
 import { Query } from '../utils';
 import './projectActivity.css';
 import ProjectActivityAnalysesList from './ProjectActivityAnalysesList';
index b11760e19264406e3379393633f39235fb9ad7dc..ed7fa1f675ca234fb357b2b149f7533b8886aacd 100644 (file)
@@ -31,8 +31,14 @@ import {
   saveActivityGraph,
   splitSeriesInGraphs
 } from '../../../components/activity-graph/utils';
-import { GraphType, MeasureHistory, Point, Serie } from '../../../types/project-activity';
-import { Metric, ParsedAnalysis } from '../../../types/types';
+import {
+  GraphType,
+  MeasureHistory,
+  ParsedAnalysis,
+  Point,
+  Serie
+} from '../../../types/project-activity';
+import { Metric } from '../../../types/types';
 import { datesQueryChanged, historyQueryChanged, Query } from '../utils';
 import { PROJECT_ACTIVITY_GRAPH } from './ProjectActivityApp';
 
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/RichQualityGateEventInner.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/RichQualityGateEventInner.tsx
deleted file mode 100644 (file)
index 37c6333..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import Link from '../../../components/common/Link';
-import { ResetButtonLink } from '../../../components/controls/buttons';
-import DropdownIcon from '../../../components/icons/DropdownIcon';
-import Level from '../../../components/ui/Level';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { getProjectUrl } from '../../../helpers/urls';
-import { AnalysisEvent } from '../../../types/types';
-
-export type RichQualityGateEvent = AnalysisEvent & Required<Pick<AnalysisEvent, 'qualityGate'>>;
-
-export function isRichQualityGateEvent(event: AnalysisEvent): event is RichQualityGateEvent {
-  return event.category === 'QUALITY_GATE' && event.qualityGate !== undefined;
-}
-
-interface Props {
-  event: RichQualityGateEvent;
-  readonly?: boolean;
-}
-
-interface State {
-  expanded: boolean;
-}
-
-export class RichQualityGateEventInner extends React.PureComponent<Props, State> {
-  state: State = { expanded: false };
-
-  stopPropagation = (event: React.MouseEvent<HTMLAnchorElement>) => {
-    event.stopPropagation();
-  };
-
-  toggleProjectsList = () => {
-    this.setState(state => ({ expanded: !state.expanded }));
-  };
-
-  render() {
-    const { event, readonly } = this.props;
-    const { expanded } = this.state;
-    return (
-      <>
-        <span className="note spacer-right">{translate('event.category', event.category)}:</span>
-        {event.qualityGate.stillFailing ? (
-          <FormattedMessage
-            defaultMessage={translate('event.quality_gate.still_x')}
-            id="event.quality_gate.still_x"
-            values={{ status: <Level level={event.qualityGate.status} small={true} /> }}
-          />
-        ) : (
-          <Level level={event.qualityGate.status} small={true} />
-        )}
-
-        <div>
-          {!readonly && event.qualityGate.failing.length > 0 && (
-            <ResetButtonLink
-              className="project-activity-event-inner-more-link"
-              onClick={this.toggleProjectsList}
-              stopPropagation={true}>
-              {expanded ? translate('hide') : translate('more')}
-              <DropdownIcon className="little-spacer-left" turned={expanded} />
-            </ResetButtonLink>
-          )}
-        </div>
-
-        {expanded && (
-          <ul className="spacer-left spacer-top">
-            {event.qualityGate.failing.map(project => (
-              <li className="display-flex-center spacer-top" key={project.key}>
-                <Level
-                  aria-label={translate('quality_gates.status')}
-                  className="spacer-right"
-                  level={event.qualityGate.status}
-                  small={true}
-                />
-                <div className="flex-1 text-ellipsis">
-                  <Link
-                    onClick={this.stopPropagation}
-                    title={project.name}
-                    to={getProjectUrl(project.key, project.branch)}>
-                    <span aria-label={translateWithParameters('project_x', project.name)}>
-                      {project.name}
-                    </span>
-                  </Link>
-                </div>
-              </li>
-            ))}
-          </ul>
-        )}
-      </>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/DefinitionChangeEventInner-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/DefinitionChangeEventInner-test.tsx
deleted file mode 100644 (file)
index 527588b..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockBranch } from '../../../../helpers/mocks/branch-like';
-import { click } from '../../../../helpers/testUtils';
-import { DefinitionChangeEvent, DefinitionChangeEventInner } from '../DefinitionChangeEventInner';
-
-it('should render', () => {
-  const event: DefinitionChangeEvent = {
-    category: 'DEFINITION_CHANGE',
-    key: 'foo1234',
-    name: '',
-    definitionChange: {
-      projects: [
-        { changeType: 'ADDED', key: 'foo', name: 'Foo', branch: 'master' },
-        { changeType: 'REMOVED', key: 'bar', name: 'Bar', branch: 'master' }
-      ]
-    }
-  };
-  const wrapper = shallow(<DefinitionChangeEventInner branchLike={undefined} event={event} />);
-  expect(wrapper).toMatchSnapshot();
-
-  click(wrapper.find('.project-activity-event-inner-more-link'));
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should render for a branch', () => {
-  const branch = mockBranch({ name: 'feature-x' });
-  const event: DefinitionChangeEvent = {
-    category: 'DEFINITION_CHANGE',
-    key: 'foo1234',
-    name: '',
-    definitionChange: {
-      projects: [
-        { changeType: 'ADDED', key: 'foo', name: 'Foo', branch: 'feature-x' },
-        {
-          changeType: 'BRANCH_CHANGED',
-          key: 'bar',
-          name: 'Bar',
-          oldBranch: 'master',
-          newBranch: 'feature-y'
-        }
-      ]
-    }
-  };
-  const wrapper = shallow(<DefinitionChangeEventInner branchLike={branch} event={event} />);
-  click(wrapper.find('.project-activity-event-inner-more-link'));
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should render when readonly', () => {
-  const event: DefinitionChangeEvent = {
-    category: 'DEFINITION_CHANGE',
-    key: 'foo1234',
-    name: '',
-    definitionChange: {
-      projects: [
-        { changeType: 'ADDED', key: 'foo', name: 'Foo', branch: 'master' },
-        { changeType: 'REMOVED', key: 'bar', name: 'Bar', branch: 'master' }
-      ]
-    }
-  };
-  const wrapper = shallow(
-    <DefinitionChangeEventInner branchLike={undefined} event={event} readonly={true} />
-  );
-  expect(wrapper).toMatchSnapshot();
-  expect(wrapper.find('.project-activity-event-inner-more-link').exists()).toBe(false);
-});
index 88d65875d7e8efaed738b87a9caf77c6a302a46d..b70d0efbd6b33b8fe5d295ee5472eaea4880e33f 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { DeleteButton, EditButton } from '../../../../components/controls/buttons';
-import { mockAnalysisEvent } from '../../../../helpers/testMocks';
+import { mockAnalysisEvent } from '../../../../helpers/mocks/project-activity';
 import { click } from '../../../../helpers/testUtils';
 import { Event, EventProps } from '../Event';
 import ChangeEventForm from '../forms/ChangeEventForm';
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/EventInner-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/EventInner-test.tsx
deleted file mode 100644 (file)
index e83d18c..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockAnalysisEvent } from '../../../../helpers/testMocks';
-import { BranchLike } from '../../../../types/branch-like';
-import EventInner, { EventInnerProps } from '../EventInner';
-
-jest.mock('../../../../app/components/componentContext/ComponentContext', () => {
-  const { mockBranch } = jest.requireActual('../../../../helpers/mocks/branch-like');
-  return {
-    ComponentContext: {
-      Consumer: ({
-        children
-      }: {
-        children: (props: { branchLike: BranchLike }) => React.ReactNode;
-      }) => {
-        return children({ branchLike: mockBranch() });
-      }
-    }
-  };
-});
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('default');
-  expect(
-    shallowRender({
-      event: mockAnalysisEvent({
-        category: 'VERSION',
-        description: undefined,
-        qualityGate: undefined
-      })
-    })
-  ).toMatchSnapshot('no description');
-  expect(shallowRender({ event: mockAnalysisEvent() })).toMatchSnapshot('rich quality gate');
-  expect(
-    shallowRender({
-      event: mockAnalysisEvent({
-        category: 'DEFINITION_CHANGE',
-        definitionChange: {
-          projects: [{ changeType: 'ADDED', key: 'foo', name: 'Foo' }]
-        },
-        qualityGate: undefined
-      })
-    })
-      .find('Consumer')
-      .dive()
-  ).toMatchSnapshot('definition change');
-});
-
-function shallowRender(props: Partial<EventInnerProps> = {}) {
-  return shallow(
-    <EventInner
-      event={mockAnalysisEvent({ category: 'VERSION', qualityGate: undefined })}
-      {...props}
-    />
-  );
-}
index cc8fda1845a6cdce5cf37336b402600b52140db1..a581ab44e67a49f4558fbc1e0adaf0c68d1b3233 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockAnalysisEvent } from '../../../../helpers/testMocks';
+import { mockAnalysisEvent } from '../../../../helpers/mocks/project-activity';
 import { Events, EventsProps } from '../Events';
 
 it('should render correctly', () => {
index 85b756aad1234679310da3acdefccbe8e7bd5bee..c0ca44e0f9a275eb0685af57d0bf2bfce17aca64 100644 (file)
@@ -21,7 +21,7 @@ import { shallow } from 'enzyme';
 import * as React from 'react';
 import { DEFAULT_GRAPH } from '../../../../components/activity-graph/utils';
 import { parseDate } from '../../../../helpers/dates';
-import { mockParsedAnalysis } from '../../../../helpers/testMocks';
+import { mockParsedAnalysis } from '../../../../helpers/mocks/project-activity';
 import { ComponentQualifier } from '../../../../types/component';
 import ProjectActivityAnalysesList from '../ProjectActivityAnalysesList';
 
index 46d7b55ce751533e993dcf3fe4635aba5034ac2e..7abfc29b7749b2a44bbf3487cde1eb550cf64b3c 100644 (file)
@@ -21,8 +21,8 @@ import { mount, shallow } from 'enzyme';
 import * as React from 'react';
 import { IntlProvider } from 'react-intl';
 import TimeFormatter from '../../../../components/intl/TimeFormatter';
+import { mockAnalysisEvent, mockParsedAnalysis } from '../../../../helpers/mocks/project-activity';
 import { scrollToElement } from '../../../../helpers/scrolling';
-import { mockAnalysisEvent, mockParsedAnalysis } from '../../../../helpers/testMocks';
 import { click } from '../../../../helpers/testUtils';
 import AddEventForm from '../forms/AddEventForm';
 import RemoveAnalysisForm from '../forms/RemoveAnalysisForm';
index 06feb27b153832e9cd84f700bc7764d88cca5fc1..377f26c803265516f54e69a7e083a74e393be446 100644 (file)
@@ -50,7 +50,8 @@ jest.mock('../../../../api/metrics', () => {
 });
 
 jest.mock('../../../../api/projectActivity', () => {
-  const { mockAnalysis, mockPaging } = jest.requireActual('../../../../helpers/testMocks');
+  const { mockPaging } = jest.requireActual('../../../../helpers/testMocks');
+  const { mockAnalysis } = jest.requireActual('../../../../helpers/mocks/project-activity');
   return {
     ...jest.requireActual('../../../../api/projectActivity'),
     createEvent: jest.fn(),
index 851afd4f53ef6c4b0f098a2b3103cee2aa2f0df3..6d5034e75fa17be0ec57017a19ee9d43a02c9c83 100644 (file)
@@ -21,12 +21,8 @@ import { shallow } from 'enzyme';
 import * as React from 'react';
 import { changeEvent, createEvent } from '../../../../api/projectActivity';
 import { mockComponent } from '../../../../helpers/mocks/component';
-import {
-  mockAnalysisEvent,
-  mockLocation,
-  mockMetric,
-  mockRouter
-} from '../../../../helpers/testMocks';
+import { mockAnalysisEvent } from '../../../../helpers/mocks/project-activity';
+import { mockLocation, mockMetric, mockRouter } from '../../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../../helpers/testUtils';
 import { ComponentQualifier } from '../../../../types/component';
 import { MetricKey } from '../../../../types/metrics';
@@ -59,7 +55,8 @@ jest.mock('../../../../api/metrics', () => {
 });
 
 jest.mock('../../../../api/projectActivity', () => {
-  const { mockAnalysis, mockPaging } = jest.requireActual('../../../../helpers/testMocks');
+  const { mockPaging } = jest.requireActual('../../../../helpers/testMocks');
+  const { mockAnalysis } = jest.requireActual('../../../../helpers/mocks/project-activity');
   return {
     ...jest.requireActual('../../../../api/projectActivity'),
     createEvent: jest.fn(),
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/RichQualityGateEventInner-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/RichQualityGateEventInner-test.tsx
deleted file mode 100644 (file)
index 5add1f4..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { click } from '../../../../helpers/testUtils';
-import { RichQualityGateEvent, RichQualityGateEventInner } from '../RichQualityGateEventInner';
-
-const event: RichQualityGateEvent = {
-  category: 'QUALITY_GATE',
-  key: 'foo1234',
-  name: '',
-  qualityGate: {
-    failing: [
-      { branch: 'master', key: 'foo', name: 'Foo' },
-      { branch: 'master', key: 'bar', name: 'Bar' }
-    ],
-    status: 'ERROR',
-    stillFailing: true
-  }
-};
-
-it('should render', () => {
-  const wrapper = shallow(<RichQualityGateEventInner event={event} />);
-  expect(wrapper).toMatchSnapshot();
-
-  click(wrapper.find('.project-activity-event-inner-more-link'));
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should not expand', () => {
-  const wrapper = shallow(
-    <RichQualityGateEventInner
-      event={{ ...event, qualityGate: { ...event.qualityGate, failing: [] } }}
-    />
-  );
-  expect(wrapper.find('.project-activity-event-inner-more-link').exists()).toBe(false);
-});
-
-it('should not expand when readonly', () => {
-  const wrapper = shallow(<RichQualityGateEventInner event={event} readonly={true} />);
-  expect(wrapper.find('.project-activity-event-inner-more-link').exists()).toBe(false);
-});
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/DefinitionChangeEventInner-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/DefinitionChangeEventInner-test.tsx.snap
deleted file mode 100644 (file)
index ce770c6..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render 1`] = `
-<Fragment>
-  <span
-    className="note"
-  >
-    event.category.DEFINITION_CHANGE
-    :
-  </span>
-  <div>
-    <ButtonLink
-      className="project-activity-event-inner-more-link"
-      onClick={[Function]}
-      stopPropagation={true}
-    >
-      more
-      <DropdownIcon
-        className="little-spacer-left"
-        turned={false}
-      />
-    </ButtonLink>
-  </div>
-</Fragment>
-`;
-
-exports[`should render 2`] = `
-<Fragment>
-  <span
-    className="note"
-  >
-    event.category.DEFINITION_CHANGE
-    :
-  </span>
-  <div>
-    <ButtonLink
-      className="project-activity-event-inner-more-link"
-      onClick={[Function]}
-      stopPropagation={true}
-    >
-      hide
-      <DropdownIcon
-        className="little-spacer-left"
-        turned={true}
-      />
-    </ButtonLink>
-  </div>
-  <ul
-    className="spacer-left spacer-top"
-  >
-    <li
-      className="display-flex-center spacer-top"
-      key="foo"
-    >
-      <div
-        className="text-ellipsis"
-      >
-        <FormattedMessage
-          defaultMessage="event.definition_change.added"
-          id="event.definition_change.added"
-          values={
-            Object {
-              "branch": <span
-                className="nowrap"
-                title="master"
-              >
-                <BranchIcon
-                  className="little-spacer-left text-text-top"
-                />
-                master
-              </span>,
-              "project": <ForwardRef(Link)
-                onClick={[Function]}
-                title="Foo"
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "search": "?id=foo&branch=master",
-                  }
-                }
-              >
-                Foo
-              </ForwardRef(Link)>,
-            }
-          }
-        />
-      </div>
-    </li>
-    <li
-      className="display-flex-center spacer-top"
-      key="bar"
-    >
-      <div
-        className="text-ellipsis"
-      >
-        <FormattedMessage
-          defaultMessage="event.definition_change.removed"
-          id="event.definition_change.removed"
-          values={
-            Object {
-              "branch": <span
-                className="nowrap"
-                title="master"
-              >
-                <BranchIcon
-                  className="little-spacer-left text-text-top"
-                />
-                master
-              </span>,
-              "project": <ForwardRef(Link)
-                onClick={[Function]}
-                title="Bar"
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "search": "?id=bar&branch=master",
-                  }
-                }
-              >
-                Bar
-              </ForwardRef(Link)>,
-            }
-          }
-        />
-      </div>
-    </li>
-  </ul>
-</Fragment>
-`;
-
-exports[`should render for a branch 1`] = `
-<Fragment>
-  <span
-    className="note"
-  >
-    event.category.DEFINITION_CHANGE
-    :
-  </span>
-  <div>
-    <ButtonLink
-      className="project-activity-event-inner-more-link"
-      onClick={[Function]}
-      stopPropagation={true}
-    >
-      hide
-      <DropdownIcon
-        className="little-spacer-left"
-        turned={true}
-      />
-    </ButtonLink>
-  </div>
-  <ul
-    className="spacer-left spacer-top"
-  >
-    <li
-      className="display-flex-center spacer-top"
-      key="foo"
-    >
-      <div
-        className="text-ellipsis"
-      >
-        <FormattedMessage
-          defaultMessage="event.definition_change.branch_added"
-          id="event.definition_change.branch_added"
-          values={
-            Object {
-              "branch": <span
-                className="nowrap"
-                title="feature-x"
-              >
-                <BranchIcon
-                  className="little-spacer-left text-text-top"
-                />
-                feature-x
-              </span>,
-              "project": <ForwardRef(Link)
-                onClick={[Function]}
-                title="Foo"
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "search": "?id=foo&branch=feature-x",
-                  }
-                }
-              >
-                Foo
-              </ForwardRef(Link)>,
-            }
-          }
-        />
-      </div>
-    </li>
-    <li
-      className="display-flex-center spacer-top"
-      key="bar"
-    >
-      <FormattedMessage
-        defaultMessage="event.definition_change.branch_replaced"
-        id="event.definition_change.branch_replaced"
-        values={
-          Object {
-            "newBranch": <span
-              className="nowrap"
-              title="feature-y"
-            >
-              <BranchIcon
-                className="little-spacer-left text-text-top"
-              />
-              feature-y
-            </span>,
-            "oldBranch": <span
-              className="nowrap"
-              title="master"
-            >
-              <BranchIcon
-                className="little-spacer-left text-text-top"
-              />
-              master
-            </span>,
-            "project": <ForwardRef(Link)
-              onClick={[Function]}
-              title="Bar"
-              to={
-                Object {
-                  "pathname": "/dashboard",
-                  "search": "?id=bar&branch=feature-y",
-                }
-              }
-            >
-              Bar
-            </ForwardRef(Link)>,
-          }
-        }
-      />
-    </li>
-  </ul>
-</Fragment>
-`;
-
-exports[`should render when readonly 1`] = `
-<Fragment>
-  <span
-    className="note"
-  >
-    event.category.DEFINITION_CHANGE
-  </span>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/EventInner-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/EventInner-test.tsx.snap
deleted file mode 100644 (file)
index 639a2bb..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<Tooltip
-  overlay="Lorem ipsum dolor sit amet"
->
-  <span
-    className="text-middle"
-  >
-    <span
-      className="note little-spacer-right"
-    >
-      event.category.VERSION
-      :
-    </span>
-    <strong
-      className="spacer-right"
-    >
-      Lorem ipsum
-    </strong>
-  </span>
-</Tooltip>
-`;
-
-exports[`should render correctly: definition change 1`] = `
-<DefinitionChangeEventInner
-  branchLike={
-    Object {
-      "analysisDate": "2018-01-01",
-      "excludedFromPurge": true,
-      "isMain": false,
-      "name": "branch-6.7",
-    }
-  }
-  event={
-    Object {
-      "category": "DEFINITION_CHANGE",
-      "definitionChange": Object {
-        "projects": Array [
-          Object {
-            "changeType": "ADDED",
-            "key": "foo",
-            "name": "Foo",
-          },
-        ],
-      },
-      "description": "Lorem ipsum dolor sit amet",
-      "key": "E11",
-      "name": "Lorem ipsum",
-      "qualityGate": undefined,
-    }
-  }
-/>
-`;
-
-exports[`should render correctly: no description 1`] = `
-<Tooltip
-  overlay={null}
->
-  <span
-    className="text-middle"
-  >
-    <span
-      className="note little-spacer-right"
-    >
-      event.category.VERSION
-      :
-    </span>
-    <strong
-      className="spacer-right"
-    >
-      Lorem ipsum
-    </strong>
-  </span>
-</Tooltip>
-`;
-
-exports[`should render correctly: rich quality gate 1`] = `
-<RichQualityGateEventInner
-  event={
-    Object {
-      "category": "QUALITY_GATE",
-      "description": "Lorem ipsum dolor sit amet",
-      "key": "E11",
-      "name": "Lorem ipsum",
-      "qualityGate": Object {
-        "failing": Array [
-          Object {
-            "branch": "master",
-            "key": "foo",
-            "name": "Foo",
-          },
-          Object {
-            "branch": "feature/bar",
-            "key": "bar",
-            "name": "Bar",
-          },
-        ],
-        "status": "ERROR",
-        "stillFailing": true,
-      },
-    }
-  }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap
deleted file mode 100644 (file)
index cd93736..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render 1`] = `
-<Fragment>
-  <span
-    className="note spacer-right"
-  >
-    event.category.QUALITY_GATE
-    :
-  </span>
-  <FormattedMessage
-    defaultMessage="event.quality_gate.still_x"
-    id="event.quality_gate.still_x"
-    values={
-      Object {
-        "status": <Level
-          level="ERROR"
-          small={true}
-        />,
-      }
-    }
-  />
-  <div>
-    <ResetButtonLink
-      className="project-activity-event-inner-more-link"
-      onClick={[Function]}
-      stopPropagation={true}
-    >
-      more
-      <DropdownIcon
-        className="little-spacer-left"
-        turned={false}
-      />
-    </ResetButtonLink>
-  </div>
-</Fragment>
-`;
-
-exports[`should render 2`] = `
-<Fragment>
-  <span
-    className="note spacer-right"
-  >
-    event.category.QUALITY_GATE
-    :
-  </span>
-  <FormattedMessage
-    defaultMessage="event.quality_gate.still_x"
-    id="event.quality_gate.still_x"
-    values={
-      Object {
-        "status": <Level
-          level="ERROR"
-          small={true}
-        />,
-      }
-    }
-  />
-  <div>
-    <ResetButtonLink
-      className="project-activity-event-inner-more-link"
-      onClick={[Function]}
-      stopPropagation={true}
-    >
-      hide
-      <DropdownIcon
-        className="little-spacer-left"
-        turned={true}
-      />
-    </ResetButtonLink>
-  </div>
-  <ul
-    className="spacer-left spacer-top"
-  >
-    <li
-      className="display-flex-center spacer-top"
-      key="foo"
-    >
-      <Level
-        aria-label="quality_gates.status"
-        className="spacer-right"
-        level="ERROR"
-        small={true}
-      />
-      <div
-        className="flex-1 text-ellipsis"
-      >
-        <ForwardRef(Link)
-          onClick={[Function]}
-          title="Foo"
-          to={
-            Object {
-              "pathname": "/dashboard",
-              "search": "?id=foo&branch=master",
-            }
-          }
-        >
-          <span
-            aria-label="project_x.Foo"
-          >
-            Foo
-          </span>
-        </ForwardRef(Link)>
-      </div>
-    </li>
-    <li
-      className="display-flex-center spacer-top"
-      key="bar"
-    >
-      <Level
-        aria-label="quality_gates.status"
-        className="spacer-right"
-        level="ERROR"
-        small={true}
-      />
-      <div
-        className="flex-1 text-ellipsis"
-      >
-        <ForwardRef(Link)
-          onClick={[Function]}
-          title="Bar"
-          to={
-            Object {
-              "pathname": "/dashboard",
-              "search": "?id=bar&branch=master",
-            }
-          }
-        >
-          <span
-            aria-label="project_x.Bar"
-          >
-            Bar
-          </span>
-        </ForwardRef(Link)>
-      </div>
-    </li>
-  </ul>
-</Fragment>
-`;
index 24299b9be1c49b51e4bcd4a26794811aaee117e0..e6e655466bc12eaa651c498bde5a933f908f7620 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import ConfirmModal from '../../../../components/controls/ConfirmModal';
 import { translate } from '../../../../helpers/l10n';
-import { ParsedAnalysis } from '../../../../types/types';
+import { ParsedAnalysis } from '../../../../types/project-activity';
 
 interface Props {
   addEvent: (analysis: string, name: string, category?: string) => Promise<void>;
index 189da0bf9c13185e7e37fb12496689d905929f5d..177e1878486b108156d2c2d232572cbe2c4c40a6 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import ConfirmModal from '../../../../components/controls/ConfirmModal';
 import { translate } from '../../../../helpers/l10n';
-import { AnalysisEvent } from '../../../../types/types';
+import { AnalysisEvent } from '../../../../types/project-activity';
 
 interface Props {
   changeEvent: (event: string, name: string) => Promise<void>;
index 8eab959af3fe4ae8fb00eea4b498fcf44798484a..f8548fd77c8737c78e5f498dc51d511861d06b1a 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import ConfirmModal from '../../../../components/controls/ConfirmModal';
 import { translate } from '../../../../helpers/l10n';
-import { ParsedAnalysis } from '../../../../types/types';
+import { ParsedAnalysis } from '../../../../types/project-activity';
 
 interface Props {
   analysis: ParsedAnalysis;
index 9f7af825d2dc7845191923ed1a4abce1d2631820..947b2ba65cd3324c2e89d37eea52d608468c4f88 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import ConfirmModal from '../../../../components/controls/ConfirmModal';
 import { translate } from '../../../../helpers/l10n';
-import { AnalysisEvent } from '../../../../types/types';
+import { AnalysisEvent } from '../../../../types/project-activity';
 
 export interface RemoveEventFormProps {
   analysisKey: string;
index 99d8d97e8da3d4daab62980eb6281187ab9be774..f0133209832409ee5595823a4b07262cfb450464 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import ConfirmModal from '../../../../../components/controls/ConfirmModal';
-import { mockAnalysisEvent } from '../../../../../helpers/testMocks';
+import { mockAnalysisEvent } from '../../../../../helpers/mocks/project-activity';
 import RemoveEventForm, { RemoveEventFormProps } from '../RemoveEventForm';
 
 it('should render correctly', () => {
index cd2c864eefc70d9d4356a641680351cc45cc602a..679b47ac1944758d67420f32503c90da575e5c36 100644 (file)
@@ -30,8 +30,8 @@ import {
   serializeString,
   serializeStringArray
 } from '../../helpers/query';
-import { GraphType } from '../../types/project-activity';
-import { Dict, ParsedAnalysis, RawQuery } from '../../types/types';
+import { GraphType, ParsedAnalysis } from '../../types/project-activity';
+import { Dict, RawQuery } from '../../types/types';
 
 export interface Query {
   category: string;
index a68bf11ed8f43d99c91b7b8f2f401acdff945cf0..17f9c960606bc4e4852f55a9cad8fc8c53bfdb2d 100644 (file)
@@ -34,12 +34,8 @@ import { translate } from '../../../helpers/l10n';
 import { AppState } from '../../../types/appstate';
 import { Branch, BranchLike } from '../../../types/branch-like';
 import { Feature } from '../../../types/features';
-import {
-  Component,
-  NewCodePeriod,
-  NewCodePeriodSettingType,
-  ParsedAnalysis
-} from '../../../types/types';
+import { ParsedAnalysis } from '../../../types/project-activity';
+import { Component, NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types';
 import '../styles.css';
 import { getSettingValue } from '../utils';
 import AppHeader from './AppHeader';
index c133dee8f1f48d1cda7ba9f7b092c37b236344b7..ac39e3d327918ec156c6e5f410337ab17fd5d8ae 100644 (file)
@@ -23,7 +23,8 @@ import * as React from 'react';
 import { getProjectActivity } from '../../../api/projectActivity';
 import { parseDate, toShortNotSoISOString } from '../../../helpers/dates';
 import { scrollToElement } from '../../../helpers/scrolling';
-import { Analysis, Dict, ParsedAnalysis } from '../../../types/types';
+import { Analysis, ParsedAnalysis } from '../../../types/project-activity';
+import { Dict } from '../../../types/types';
 import BranchAnalysisListRenderer from './BranchAnalysisListRenderer';
 
 interface Props {
index f33c1ddb72a2e0f28820efd899b74750a0c2c583..59e9734ab6cd2bd68fd32cc3b192abfbbd529b82 100644 (file)
@@ -27,7 +27,7 @@ import TimeFormatter from '../../../components/intl/TimeFormatter';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { parseDate, toShortNotSoISOString } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
-import { ParsedAnalysis } from '../../../types/types';
+import { ParsedAnalysis } from '../../../types/project-activity';
 import Events from '../../projectActivity/components/Events';
 import { getAnalysesByVersionByDay } from '../../projectActivity/utils';
 
index b3b074509f499c7ad01486ab1b96b08268ab793f..30fcb1080c07c2b464d244c01fe1dd874cfd7454 100644 (file)
@@ -25,7 +25,8 @@ import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { toNotSoISOString } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Branch, BranchWithNewCodePeriod } from '../../../types/branch-like';
-import { NewCodePeriod, NewCodePeriodSettingType, ParsedAnalysis } from '../../../types/types';
+import { ParsedAnalysis } from '../../../types/project-activity';
+import { NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types';
 import { getSettingValue, validateSetting } from '../utils';
 import BaselineSettingAnalysis from './BaselineSettingAnalysis';
 import BaselineSettingDays from './BaselineSettingDays';
index 190f93fcb13a16cd40a74cfc17f9daec42dcd72e..724d16a602074b80e433f199f1facc732901671e 100644 (file)
@@ -25,7 +25,8 @@ import { Alert } from '../../../components/ui/Alert';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Branch } from '../../../types/branch-like';
-import { NewCodePeriod, NewCodePeriodSettingType, ParsedAnalysis } from '../../../types/types';
+import { ParsedAnalysis } from '../../../types/project-activity';
+import { NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types';
 import { validateSetting } from '../utils';
 import BaselineSettingAnalysis from './BaselineSettingAnalysis';
 import BaselineSettingDays from './BaselineSettingDays';
index 72ba24597a0d6612699cd0440139832e0a8b7a1a..17d935e0480c7fc7b55a5d367fd8c1b233adadc4 100644 (file)
@@ -22,7 +22,7 @@ import { shallow } from 'enzyme';
 import * as React from 'react';
 import { getProjectActivity } from '../../../../api/projectActivity';
 import { toShortNotSoISOString } from '../../../../helpers/dates';
-import { mockAnalysis, mockAnalysisEvent } from '../../../../helpers/testMocks';
+import { mockAnalysis, mockAnalysisEvent } from '../../../../helpers/mocks/project-activity';
 import { waitAndUpdate } from '../../../../helpers/testUtils';
 import BranchAnalysisList from '../BranchAnalysisList';
 
index 98c9fab01e5d6a83765d0f6514a51a60f1fb8aad..1fa07679e2cad19b201127be27e3de04805afcfb 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockAnalysisEvent, mockParsedAnalysis } from '../../../../helpers/testMocks';
+import { mockAnalysisEvent, mockParsedAnalysis } from '../../../../helpers/mocks/project-activity';
 import BranchAnalysisListRenderer, {
   BranchAnalysisListRendererProps
 } from '../BranchAnalysisListRenderer';
index 8cfea76f95cecb4c462bb50fb242d7fa345a2902..ef88cf835861940773befedc604d7baea7a81d79 100644 (file)
@@ -20,7 +20,6 @@
 import { filter, slice, sortBy } from 'lodash';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
-import EventInner from '../../apps/projectActivity/components/EventInner';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { formatMeasure } from '../../helpers/measures';
 import { ParsedAnalysis, Serie } from '../../types/project-activity';
@@ -29,6 +28,7 @@ import Modal from '../controls/Modal';
 import DateFormatter from '../intl/DateFormatter';
 import TimeFormatter from '../intl/TimeFormatter';
 import { Alert } from '../ui/Alert';
+import EventInner from './EventInner';
 import { getAnalysisEventsForDate } from './utils';
 
 export interface DataTableModalProps {
diff --git a/server/sonar-web/src/main/js/components/activity-graph/DefinitionChangeEventInner.tsx b/server/sonar-web/src/main/js/components/activity-graph/DefinitionChangeEventInner.tsx
new file mode 100644 (file)
index 0000000..7d52c9c
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { isMainBranch } from '../../helpers/branch-like';
+import { translate } from '../../helpers/l10n';
+import { limitComponentName } from '../../helpers/path';
+import { getProjectUrl } from '../../helpers/urls';
+import { BranchLike } from '../../types/branch-like';
+import { AnalysisEvent } from '../../types/project-activity';
+import Link from '../common/Link';
+import { ButtonLink } from '../controls/buttons';
+import BranchIcon from '../icons/BranchIcon';
+import DropdownIcon from '../icons/DropdownIcon';
+
+export type DefinitionChangeEvent = AnalysisEvent &
+  Required<Pick<AnalysisEvent, 'definitionChange'>>;
+
+export function isDefinitionChangeEvent(event: AnalysisEvent): event is DefinitionChangeEvent {
+  return event.category === 'DEFINITION_CHANGE' && event.definitionChange !== undefined;
+}
+
+interface Props {
+  branchLike: BranchLike | undefined;
+  event: DefinitionChangeEvent;
+  readonly?: boolean;
+}
+
+interface State {
+  expanded: boolean;
+}
+
+export class DefinitionChangeEventInner extends React.PureComponent<Props, State> {
+  state: State = { expanded: false };
+
+  stopPropagation = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.stopPropagation();
+  };
+
+  toggleProjectsList = () => {
+    this.setState(state => ({ expanded: !state.expanded }));
+  };
+
+  renderProjectLink = (project: { key: string; name: string }, branch: string | undefined) => (
+    <Link
+      onClick={this.stopPropagation}
+      title={project.name}
+      to={getProjectUrl(project.key, branch)}>
+      {limitComponentName(project.name, 28)}
+    </Link>
+  );
+
+  renderBranch = (branch = translate('branches.main_branch')) => (
+    <span className="nowrap" title={branch}>
+      <BranchIcon className="little-spacer-left text-text-top" />
+      {branch}
+    </span>
+  );
+
+  renderProjectChange(project: {
+    changeType: string;
+    key: string;
+    name: string;
+    branch?: string;
+    newBranch?: string;
+    oldBranch?: string;
+  }) {
+    const mainBranch = !this.props.branchLike || isMainBranch(this.props.branchLike);
+
+    if (project.changeType === 'ADDED') {
+      const message = mainBranch
+        ? 'event.definition_change.added'
+        : 'event.definition_change.branch_added';
+      return (
+        <div className="text-ellipsis">
+          <FormattedMessage
+            defaultMessage={translate(message)}
+            id={message}
+            values={{
+              project: this.renderProjectLink(project, project.branch),
+              branch: this.renderBranch(project.branch)
+            }}
+          />
+        </div>
+      );
+    } else if (project.changeType === 'REMOVED') {
+      const message = mainBranch
+        ? 'event.definition_change.removed'
+        : 'event.definition_change.branch_removed';
+      return (
+        <div className="text-ellipsis">
+          <FormattedMessage
+            defaultMessage={translate(message)}
+            id={message}
+            values={{
+              project: this.renderProjectLink(project, project.branch),
+              branch: this.renderBranch(project.branch)
+            }}
+          />
+        </div>
+      );
+    } else if (project.changeType === 'BRANCH_CHANGED') {
+      return (
+        <FormattedMessage
+          defaultMessage={translate('event.definition_change.branch_replaced')}
+          id="event.definition_change.branch_replaced"
+          values={{
+            project: this.renderProjectLink(project, project.newBranch),
+            oldBranch: this.renderBranch(project.oldBranch),
+            newBranch: this.renderBranch(project.newBranch)
+          }}
+        />
+      );
+    }
+    return null;
+  }
+
+  render() {
+    const { event, readonly } = this.props;
+    const { expanded } = this.state;
+    return (
+      <>
+        <span className="note">
+          {translate('event.category', event.category)}
+          {!readonly && ':'}
+        </span>
+
+        {!readonly && (
+          <div>
+            <ButtonLink
+              className="project-activity-event-inner-more-link"
+              onClick={this.toggleProjectsList}
+              stopPropagation={true}>
+              {expanded ? translate('hide') : translate('more')}
+              <DropdownIcon className="little-spacer-left" turned={expanded} />
+            </ButtonLink>
+          </div>
+        )}
+
+        {expanded && (
+          <ul className="spacer-left spacer-top">
+            {event.definitionChange.projects.map(project => (
+              <li className="display-flex-center spacer-top" key={project.key}>
+                {this.renderProjectChange(project)}
+              </li>
+            ))}
+          </ul>
+        )}
+      </>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx b/server/sonar-web/src/main/js/components/activity-graph/EventInner.tsx
new file mode 100644 (file)
index 0000000..b4dc517
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { ComponentContext } from '../../app/components/componentContext/ComponentContext';
+import { translate } from '../../helpers/l10n';
+import { AnalysisEvent } from '../../types/project-activity';
+import Tooltip from '../controls/Tooltip';
+import { DefinitionChangeEventInner, isDefinitionChangeEvent } from './DefinitionChangeEventInner';
+import { isRichQualityGateEvent, RichQualityGateEventInner } from './RichQualityGateEventInner';
+
+export interface EventInnerProps {
+  event: AnalysisEvent;
+  readonly?: boolean;
+}
+
+export default function EventInner({ event, readonly }: EventInnerProps) {
+  if (isRichQualityGateEvent(event)) {
+    return <RichQualityGateEventInner event={event} readonly={readonly} />;
+  } else if (isDefinitionChangeEvent(event)) {
+    return (
+      <ComponentContext.Consumer>
+        {({ branchLike }) => (
+          <DefinitionChangeEventInner branchLike={branchLike} event={event} readonly={readonly} />
+        )}
+      </ComponentContext.Consumer>
+    );
+  }
+
+  return (
+    <Tooltip overlay={event.description || null}>
+      <span className="text-middle">
+        <span className="note little-spacer-right">
+          {translate('event.category', event.category)}:
+        </span>
+        <strong className="spacer-right">{event.name}</strong>
+      </span>
+    </Tooltip>
+  );
+}
index f19056711d39186b5f720b4ff96baf869cd806ef..bb674ec3a212f6643852226422f7e43ef0caedeb 100644 (file)
@@ -22,8 +22,7 @@ import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer';
 import AdvancedTimeline from '../../components/charts/AdvancedTimeline';
 import { translate } from '../../helpers/l10n';
 import { formatMeasure, getShortType } from '../../helpers/measures';
-import { MeasureHistory, Serie } from '../../types/project-activity';
-import { ParsedAnalysis } from '../../types/types';
+import { MeasureHistory, ParsedAnalysis, Serie } from '../../types/project-activity';
 import { Button } from '../controls/buttons';
 import ModalButton from '../controls/ModalButton';
 import DataTableModal from './DataTableModal';
index 288bd4773b329cb6cf92412acd9a5d1c242bc988..d4924ebb2beea48b04043f8381e8d920d3cda918 100644 (file)
@@ -22,8 +22,7 @@ import * as React from 'react';
 import DeferredSpinner from '../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { getBaseUrl } from '../../helpers/system';
-import { GraphType, MeasureHistory, Serie } from '../../types/project-activity';
-import { ParsedAnalysis } from '../../types/types';
+import { GraphType, MeasureHistory, ParsedAnalysis, Serie } from '../../types/project-activity';
 import GraphHistory from './GraphHistory';
 import './styles.css';
 import { getSeriesMetricType, hasHistoryData, isCustomGraph } from './utils';
index 25c93ea2fef943e3f28ced4ddc9cec62c3e7cf06..b7d49b60cd5c25d483847cbf19bff26d7f945c60 100644 (file)
@@ -20,8 +20,7 @@
 import * as React from 'react';
 import { Popup, PopupPlacement } from '../../components/ui/popups';
 import { isDefined } from '../../helpers/types';
-import { MeasureHistory, Serie } from '../../types/project-activity';
-import { AnalysisEvent } from '../../types/types';
+import { AnalysisEvent, MeasureHistory, Serie } from '../../types/project-activity';
 import DateTimeFormatter from '../intl/DateTimeFormatter';
 import GraphsTooltipsContent from './GraphsTooltipsContent';
 import GraphsTooltipsContentCoverage from './GraphsTooltipsContentCoverage';
index 466e0c1edf82daa4d6929a56275fbead3ecfb775..85cafa0761bf089f6961686c34b5243a55242571 100644 (file)
@@ -18,8 +18,8 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import EventInner from '../../apps/projectActivity/components/EventInner';
-import { AnalysisEvent } from '../../types/types';
+import { AnalysisEvent } from '../../types/project-activity';
+import EventInner from './EventInner';
 
 interface Props {
   addSeparator: boolean;
diff --git a/server/sonar-web/src/main/js/components/activity-graph/RichQualityGateEventInner.tsx b/server/sonar-web/src/main/js/components/activity-graph/RichQualityGateEventInner.tsx
new file mode 100644 (file)
index 0000000..2f81862
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+import { getProjectUrl } from '../../helpers/urls';
+import { AnalysisEvent } from '../../types/project-activity';
+import Link from '../common/Link';
+import { ResetButtonLink } from '../controls/buttons';
+import DropdownIcon from '../icons/DropdownIcon';
+import Level from '../ui/Level';
+
+export type RichQualityGateEvent = AnalysisEvent & Required<Pick<AnalysisEvent, 'qualityGate'>>;
+
+export function isRichQualityGateEvent(event: AnalysisEvent): event is RichQualityGateEvent {
+  return event.category === 'QUALITY_GATE' && event.qualityGate !== undefined;
+}
+
+interface Props {
+  event: RichQualityGateEvent;
+  readonly?: boolean;
+}
+
+interface State {
+  expanded: boolean;
+}
+
+export class RichQualityGateEventInner extends React.PureComponent<Props, State> {
+  state: State = { expanded: false };
+
+  stopPropagation = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.stopPropagation();
+  };
+
+  toggleProjectsList = () => {
+    this.setState(state => ({ expanded: !state.expanded }));
+  };
+
+  render() {
+    const { event, readonly } = this.props;
+    const { expanded } = this.state;
+    return (
+      <>
+        <span className="note spacer-right">{translate('event.category', event.category)}:</span>
+        {event.qualityGate.stillFailing ? (
+          <FormattedMessage
+            defaultMessage={translate('event.quality_gate.still_x')}
+            id="event.quality_gate.still_x"
+            values={{ status: <Level level={event.qualityGate.status} small={true} /> }}
+          />
+        ) : (
+          <Level level={event.qualityGate.status} small={true} />
+        )}
+
+        <div>
+          {!readonly && event.qualityGate.failing.length > 0 && (
+            <ResetButtonLink
+              className="project-activity-event-inner-more-link"
+              onClick={this.toggleProjectsList}
+              stopPropagation={true}>
+              {expanded ? translate('hide') : translate('more')}
+              <DropdownIcon className="little-spacer-left" turned={expanded} />
+            </ResetButtonLink>
+          )}
+        </div>
+
+        {expanded && (
+          <ul className="spacer-left spacer-top">
+            {event.qualityGate.failing.map(project => (
+              <li className="display-flex-center spacer-top" key={project.key}>
+                <Level
+                  aria-label={translate('quality_gates.status')}
+                  className="spacer-right"
+                  level={event.qualityGate.status}
+                  small={true}
+                />
+                <div className="flex-1 text-ellipsis">
+                  <Link
+                    onClick={this.stopPropagation}
+                    title={project.name}
+                    to={getProjectUrl(project.key, project.branch)}>
+                    <span aria-label={translateWithParameters('project_x', project.name)}>
+                      {project.name}
+                    </span>
+                  </Link>
+                </div>
+              </li>
+            ))}
+          </ul>
+        )}
+      </>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/DefinitionChangeEventInner-test.tsx b/server/sonar-web/src/main/js/components/activity-graph/__tests__/DefinitionChangeEventInner-test.tsx
new file mode 100644 (file)
index 0000000..9c5d0a6
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockBranch } from '../../../helpers/mocks/branch-like';
+import { click } from '../../../helpers/testUtils';
+import { DefinitionChangeEvent, DefinitionChangeEventInner } from '../DefinitionChangeEventInner';
+
+it('should render', () => {
+  const event: DefinitionChangeEvent = {
+    category: 'DEFINITION_CHANGE',
+    key: 'foo1234',
+    name: '',
+    definitionChange: {
+      projects: [
+        { changeType: 'ADDED', key: 'foo', name: 'Foo', branch: 'master' },
+        { changeType: 'REMOVED', key: 'bar', name: 'Bar', branch: 'master' }
+      ]
+    }
+  };
+  const wrapper = shallow(<DefinitionChangeEventInner branchLike={undefined} event={event} />);
+  expect(wrapper).toMatchSnapshot();
+
+  click(wrapper.find('.project-activity-event-inner-more-link'));
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('should render for a branch', () => {
+  const branch = mockBranch({ name: 'feature-x' });
+  const event: DefinitionChangeEvent = {
+    category: 'DEFINITION_CHANGE',
+    key: 'foo1234',
+    name: '',
+    definitionChange: {
+      projects: [
+        { changeType: 'ADDED', key: 'foo', name: 'Foo', branch: 'feature-x' },
+        {
+          changeType: 'BRANCH_CHANGED',
+          key: 'bar',
+          name: 'Bar',
+          oldBranch: 'master',
+          newBranch: 'feature-y'
+        }
+      ]
+    }
+  };
+  const wrapper = shallow(<DefinitionChangeEventInner branchLike={branch} event={event} />);
+  click(wrapper.find('.project-activity-event-inner-more-link'));
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('should render when readonly', () => {
+  const event: DefinitionChangeEvent = {
+    category: 'DEFINITION_CHANGE',
+    key: 'foo1234',
+    name: '',
+    definitionChange: {
+      projects: [
+        { changeType: 'ADDED', key: 'foo', name: 'Foo', branch: 'master' },
+        { changeType: 'REMOVED', key: 'bar', name: 'Bar', branch: 'master' }
+      ]
+    }
+  };
+  const wrapper = shallow(
+    <DefinitionChangeEventInner branchLike={undefined} event={event} readonly={true} />
+  );
+  expect(wrapper).toMatchSnapshot();
+  expect(wrapper.find('.project-activity-event-inner-more-link').exists()).toBe(false);
+});
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/EventInner-test.tsx b/server/sonar-web/src/main/js/components/activity-graph/__tests__/EventInner-test.tsx
new file mode 100644 (file)
index 0000000..950e389
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAnalysisEvent } from '../../../helpers/mocks/project-activity';
+import { BranchLike } from '../../../types/branch-like';
+import EventInner, { EventInnerProps } from '../EventInner';
+
+jest.mock('../../../app/components/componentContext/ComponentContext', () => {
+  const { mockBranch } = jest.requireActual('../../../helpers/mocks/branch-like');
+  return {
+    ComponentContext: {
+      Consumer: ({
+        children
+      }: {
+        children: (props: { branchLike: BranchLike }) => React.ReactNode;
+      }) => {
+        return children({ branchLike: mockBranch() });
+      }
+    }
+  };
+});
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+  expect(
+    shallowRender({
+      event: mockAnalysisEvent({
+        category: 'VERSION',
+        description: undefined,
+        qualityGate: undefined
+      })
+    })
+  ).toMatchSnapshot('no description');
+  expect(shallowRender({ event: mockAnalysisEvent() })).toMatchSnapshot('rich quality gate');
+  expect(
+    shallowRender({
+      event: mockAnalysisEvent({
+        category: 'DEFINITION_CHANGE',
+        definitionChange: {
+          projects: [{ changeType: 'ADDED', key: 'foo', name: 'Foo' }]
+        },
+        qualityGate: undefined
+      })
+    })
+      .find('Consumer')
+      .dive()
+  ).toMatchSnapshot('definition change');
+});
+
+function shallowRender(props: Partial<EventInnerProps> = {}) {
+  return shallow(
+    <EventInner
+      event={mockAnalysisEvent({ category: 'VERSION', qualityGate: undefined })}
+      {...props}
+    />
+  );
+}
index cd141b7a13db5796d3a681c64e29f53192772763..7cd02bd75b89b9ff14b3ff5e75ba0a036b21818e 100644 (file)
@@ -44,7 +44,8 @@ it('should correctly render a graph', () => {
 function shallowRender(overrides: Partial<GraphHistory['props']> = {}) {
   return shallow(
     <GraphHistory
-      events={[]}
+      analyses={[]}
+      ariaLabel="foo"
       graph={DEFAULT_GRAPH}
       leakPeriodDate={parseDate('2017-05-16T13:50:02+0200')}
       isCustom={false}
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/RichQualityGateEventInner-test.tsx b/server/sonar-web/src/main/js/components/activity-graph/__tests__/RichQualityGateEventInner-test.tsx
new file mode 100644 (file)
index 0000000..b4cd59e
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { click } from '../../../helpers/testUtils';
+import { RichQualityGateEvent, RichQualityGateEventInner } from '../RichQualityGateEventInner';
+
+const event: RichQualityGateEvent = {
+  category: 'QUALITY_GATE',
+  key: 'foo1234',
+  name: '',
+  qualityGate: {
+    failing: [
+      { branch: 'master', key: 'foo', name: 'Foo' },
+      { branch: 'master', key: 'bar', name: 'Bar' }
+    ],
+    status: 'ERROR',
+    stillFailing: true
+  }
+};
+
+it('should render', () => {
+  const wrapper = shallow(<RichQualityGateEventInner event={event} />);
+  expect(wrapper).toMatchSnapshot();
+
+  click(wrapper.find('.project-activity-event-inner-more-link'));
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('should not expand', () => {
+  const wrapper = shallow(
+    <RichQualityGateEventInner
+      event={{ ...event, qualityGate: { ...event.qualityGate, failing: [] } }}
+    />
+  );
+  expect(wrapper.find('.project-activity-event-inner-more-link').exists()).toBe(false);
+});
+
+it('should not expand when readonly', () => {
+  const wrapper = shallow(<RichQualityGateEventInner event={event} readonly={true} />);
+  expect(wrapper.find('.project-activity-event-inner-more-link').exists()).toBe(false);
+});
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/DefinitionChangeEventInner-test.tsx.snap b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/DefinitionChangeEventInner-test.tsx.snap
new file mode 100644 (file)
index 0000000..ce770c6
--- /dev/null
@@ -0,0 +1,248 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render 1`] = `
+<Fragment>
+  <span
+    className="note"
+  >
+    event.category.DEFINITION_CHANGE
+    :
+  </span>
+  <div>
+    <ButtonLink
+      className="project-activity-event-inner-more-link"
+      onClick={[Function]}
+      stopPropagation={true}
+    >
+      more
+      <DropdownIcon
+        className="little-spacer-left"
+        turned={false}
+      />
+    </ButtonLink>
+  </div>
+</Fragment>
+`;
+
+exports[`should render 2`] = `
+<Fragment>
+  <span
+    className="note"
+  >
+    event.category.DEFINITION_CHANGE
+    :
+  </span>
+  <div>
+    <ButtonLink
+      className="project-activity-event-inner-more-link"
+      onClick={[Function]}
+      stopPropagation={true}
+    >
+      hide
+      <DropdownIcon
+        className="little-spacer-left"
+        turned={true}
+      />
+    </ButtonLink>
+  </div>
+  <ul
+    className="spacer-left spacer-top"
+  >
+    <li
+      className="display-flex-center spacer-top"
+      key="foo"
+    >
+      <div
+        className="text-ellipsis"
+      >
+        <FormattedMessage
+          defaultMessage="event.definition_change.added"
+          id="event.definition_change.added"
+          values={
+            Object {
+              "branch": <span
+                className="nowrap"
+                title="master"
+              >
+                <BranchIcon
+                  className="little-spacer-left text-text-top"
+                />
+                master
+              </span>,
+              "project": <ForwardRef(Link)
+                onClick={[Function]}
+                title="Foo"
+                to={
+                  Object {
+                    "pathname": "/dashboard",
+                    "search": "?id=foo&branch=master",
+                  }
+                }
+              >
+                Foo
+              </ForwardRef(Link)>,
+            }
+          }
+        />
+      </div>
+    </li>
+    <li
+      className="display-flex-center spacer-top"
+      key="bar"
+    >
+      <div
+        className="text-ellipsis"
+      >
+        <FormattedMessage
+          defaultMessage="event.definition_change.removed"
+          id="event.definition_change.removed"
+          values={
+            Object {
+              "branch": <span
+                className="nowrap"
+                title="master"
+              >
+                <BranchIcon
+                  className="little-spacer-left text-text-top"
+                />
+                master
+              </span>,
+              "project": <ForwardRef(Link)
+                onClick={[Function]}
+                title="Bar"
+                to={
+                  Object {
+                    "pathname": "/dashboard",
+                    "search": "?id=bar&branch=master",
+                  }
+                }
+              >
+                Bar
+              </ForwardRef(Link)>,
+            }
+          }
+        />
+      </div>
+    </li>
+  </ul>
+</Fragment>
+`;
+
+exports[`should render for a branch 1`] = `
+<Fragment>
+  <span
+    className="note"
+  >
+    event.category.DEFINITION_CHANGE
+    :
+  </span>
+  <div>
+    <ButtonLink
+      className="project-activity-event-inner-more-link"
+      onClick={[Function]}
+      stopPropagation={true}
+    >
+      hide
+      <DropdownIcon
+        className="little-spacer-left"
+        turned={true}
+      />
+    </ButtonLink>
+  </div>
+  <ul
+    className="spacer-left spacer-top"
+  >
+    <li
+      className="display-flex-center spacer-top"
+      key="foo"
+    >
+      <div
+        className="text-ellipsis"
+      >
+        <FormattedMessage
+          defaultMessage="event.definition_change.branch_added"
+          id="event.definition_change.branch_added"
+          values={
+            Object {
+              "branch": <span
+                className="nowrap"
+                title="feature-x"
+              >
+                <BranchIcon
+                  className="little-spacer-left text-text-top"
+                />
+                feature-x
+              </span>,
+              "project": <ForwardRef(Link)
+                onClick={[Function]}
+                title="Foo"
+                to={
+                  Object {
+                    "pathname": "/dashboard",
+                    "search": "?id=foo&branch=feature-x",
+                  }
+                }
+              >
+                Foo
+              </ForwardRef(Link)>,
+            }
+          }
+        />
+      </div>
+    </li>
+    <li
+      className="display-flex-center spacer-top"
+      key="bar"
+    >
+      <FormattedMessage
+        defaultMessage="event.definition_change.branch_replaced"
+        id="event.definition_change.branch_replaced"
+        values={
+          Object {
+            "newBranch": <span
+              className="nowrap"
+              title="feature-y"
+            >
+              <BranchIcon
+                className="little-spacer-left text-text-top"
+              />
+              feature-y
+            </span>,
+            "oldBranch": <span
+              className="nowrap"
+              title="master"
+            >
+              <BranchIcon
+                className="little-spacer-left text-text-top"
+              />
+              master
+            </span>,
+            "project": <ForwardRef(Link)
+              onClick={[Function]}
+              title="Bar"
+              to={
+                Object {
+                  "pathname": "/dashboard",
+                  "search": "?id=bar&branch=feature-y",
+                }
+              }
+            >
+              Bar
+            </ForwardRef(Link)>,
+          }
+        }
+      />
+    </li>
+  </ul>
+</Fragment>
+`;
+
+exports[`should render when readonly 1`] = `
+<Fragment>
+  <span
+    className="note"
+  >
+    event.category.DEFINITION_CHANGE
+  </span>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/EventInner-test.tsx.snap b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/EventInner-test.tsx.snap
new file mode 100644 (file)
index 0000000..639a2bb
--- /dev/null
@@ -0,0 +1,105 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<Tooltip
+  overlay="Lorem ipsum dolor sit amet"
+>
+  <span
+    className="text-middle"
+  >
+    <span
+      className="note little-spacer-right"
+    >
+      event.category.VERSION
+      :
+    </span>
+    <strong
+      className="spacer-right"
+    >
+      Lorem ipsum
+    </strong>
+  </span>
+</Tooltip>
+`;
+
+exports[`should render correctly: definition change 1`] = `
+<DefinitionChangeEventInner
+  branchLike={
+    Object {
+      "analysisDate": "2018-01-01",
+      "excludedFromPurge": true,
+      "isMain": false,
+      "name": "branch-6.7",
+    }
+  }
+  event={
+    Object {
+      "category": "DEFINITION_CHANGE",
+      "definitionChange": Object {
+        "projects": Array [
+          Object {
+            "changeType": "ADDED",
+            "key": "foo",
+            "name": "Foo",
+          },
+        ],
+      },
+      "description": "Lorem ipsum dolor sit amet",
+      "key": "E11",
+      "name": "Lorem ipsum",
+      "qualityGate": undefined,
+    }
+  }
+/>
+`;
+
+exports[`should render correctly: no description 1`] = `
+<Tooltip
+  overlay={null}
+>
+  <span
+    className="text-middle"
+  >
+    <span
+      className="note little-spacer-right"
+    >
+      event.category.VERSION
+      :
+    </span>
+    <strong
+      className="spacer-right"
+    >
+      Lorem ipsum
+    </strong>
+  </span>
+</Tooltip>
+`;
+
+exports[`should render correctly: rich quality gate 1`] = `
+<RichQualityGateEventInner
+  event={
+    Object {
+      "category": "QUALITY_GATE",
+      "description": "Lorem ipsum dolor sit amet",
+      "key": "E11",
+      "name": "Lorem ipsum",
+      "qualityGate": Object {
+        "failing": Array [
+          Object {
+            "branch": "master",
+            "key": "foo",
+            "name": "Foo",
+          },
+          Object {
+            "branch": "feature/bar",
+            "key": "bar",
+            "name": "Bar",
+          },
+        ],
+        "status": "ERROR",
+        "stillFailing": true,
+      },
+    }
+  }
+/>
+`;
index 5c0af0d142523dc24636fbac472073b578f2e04b..f1443c67c06be61f3d7389d838e64ed5acd65ce0 100644 (file)
@@ -37,10 +37,10 @@ exports[`should correctly render a graph 1`] = `
     </AutoSizer>
   </div>
   <div
-    className="spacer-top big-spacer-bottom small"
+    className="little-spacer-top big-spacer-bottom"
   >
     <div
-      className="display-flex-justify-center"
+      className="display-flex-justify-end little-padded-right"
     >
       <ModalButton
         modal={[Function]}
@@ -90,10 +90,10 @@ exports[`should correctly render a graph: custom 1`] = `
     </AutoSizer>
   </div>
   <div
-    className="spacer-top big-spacer-bottom small"
+    className="little-spacer-top big-spacer-bottom"
   >
     <div
-      className="display-flex-justify-center"
+      className="display-flex-justify-end little-padded-right"
     >
       <ModalButton
         modal={[Function]}
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap b/server/sonar-web/src/main/js/components/activity-graph/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap
new file mode 100644 (file)
index 0000000..cd93736
--- /dev/null
@@ -0,0 +1,139 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render 1`] = `
+<Fragment>
+  <span
+    className="note spacer-right"
+  >
+    event.category.QUALITY_GATE
+    :
+  </span>
+  <FormattedMessage
+    defaultMessage="event.quality_gate.still_x"
+    id="event.quality_gate.still_x"
+    values={
+      Object {
+        "status": <Level
+          level="ERROR"
+          small={true}
+        />,
+      }
+    }
+  />
+  <div>
+    <ResetButtonLink
+      className="project-activity-event-inner-more-link"
+      onClick={[Function]}
+      stopPropagation={true}
+    >
+      more
+      <DropdownIcon
+        className="little-spacer-left"
+        turned={false}
+      />
+    </ResetButtonLink>
+  </div>
+</Fragment>
+`;
+
+exports[`should render 2`] = `
+<Fragment>
+  <span
+    className="note spacer-right"
+  >
+    event.category.QUALITY_GATE
+    :
+  </span>
+  <FormattedMessage
+    defaultMessage="event.quality_gate.still_x"
+    id="event.quality_gate.still_x"
+    values={
+      Object {
+        "status": <Level
+          level="ERROR"
+          small={true}
+        />,
+      }
+    }
+  />
+  <div>
+    <ResetButtonLink
+      className="project-activity-event-inner-more-link"
+      onClick={[Function]}
+      stopPropagation={true}
+    >
+      hide
+      <DropdownIcon
+        className="little-spacer-left"
+        turned={true}
+      />
+    </ResetButtonLink>
+  </div>
+  <ul
+    className="spacer-left spacer-top"
+  >
+    <li
+      className="display-flex-center spacer-top"
+      key="foo"
+    >
+      <Level
+        aria-label="quality_gates.status"
+        className="spacer-right"
+        level="ERROR"
+        small={true}
+      />
+      <div
+        className="flex-1 text-ellipsis"
+      >
+        <ForwardRef(Link)
+          onClick={[Function]}
+          title="Foo"
+          to={
+            Object {
+              "pathname": "/dashboard",
+              "search": "?id=foo&branch=master",
+            }
+          }
+        >
+          <span
+            aria-label="project_x.Foo"
+          >
+            Foo
+          </span>
+        </ForwardRef(Link)>
+      </div>
+    </li>
+    <li
+      className="display-flex-center spacer-top"
+      key="bar"
+    >
+      <Level
+        aria-label="quality_gates.status"
+        className="spacer-right"
+        level="ERROR"
+        small={true}
+      />
+      <div
+        className="flex-1 text-ellipsis"
+      >
+        <ForwardRef(Link)
+          onClick={[Function]}
+          title="Bar"
+          to={
+            Object {
+              "pathname": "/dashboard",
+              "search": "?id=bar&branch=master",
+            }
+          }
+        >
+          <span
+            aria-label="project_x.Bar"
+          >
+            Bar
+          </span>
+        </ForwardRef(Link)>
+      </div>
+    </li>
+  </ul>
+</Fragment>
+`;
index 46e3714d69615fa2c15f7a5059ccc14304d769e4..4c84246e277788badce480074d72e0b69de27337 100644 (file)
@@ -22,8 +22,8 @@ import { getLocalizedMetricName, translate } from '../../helpers/l10n';
 import { localizeMetric } from '../../helpers/measures';
 import { get, save } from '../../helpers/storage';
 import { MetricKey } from '../../types/metrics';
-import { GraphType, MeasureHistory, Serie } from '../../types/project-activity';
-import { Dict, Metric, ParsedAnalysis } from '../../types/types';
+import { GraphType, MeasureHistory, ParsedAnalysis, Serie } from '../../types/project-activity';
+import { Dict, Metric } from '../../types/types';
 
 export const DEFAULT_GRAPH = GraphType.issues;
 
diff --git a/server/sonar-web/src/main/js/helpers/mocks/project-activity.ts b/server/sonar-web/src/main/js/helpers/mocks/project-activity.ts
new file mode 100644 (file)
index 0000000..439bf4a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import { Analysis, AnalysisEvent, ParsedAnalysis } from '../../types/project-activity';
+
+export function mockAnalysis(overrides: Partial<Analysis> = {}): Analysis {
+  return {
+    date: '2017-03-01T09:36:01+0100',
+    events: [],
+    key: 'foo',
+    projectVersion: '1.0',
+    ...overrides
+  };
+}
+
+export function mockParsedAnalysis(overrides: Partial<ParsedAnalysis> = {}): ParsedAnalysis {
+  return {
+    date: new Date('2017-03-01T09:37:01+0100'),
+    events: [],
+    key: 'foo',
+    projectVersion: '1.0',
+    ...overrides
+  };
+}
+
+export function mockAnalysisEvent(overrides: Partial<AnalysisEvent> = {}): AnalysisEvent {
+  return {
+    category: 'QUALITY_GATE',
+    key: 'E11',
+    description: 'Lorem ipsum dolor sit amet',
+    name: 'Lorem ipsum',
+    qualityGate: {
+      status: 'ERROR',
+      stillFailing: true,
+      failing: [
+        {
+          key: 'foo',
+          name: 'Foo',
+          branch: 'master'
+        },
+        {
+          key: 'bar',
+          name: 'Bar',
+          branch: 'feature/bar'
+        }
+      ]
+    },
+    ...overrides
+  };
+}
index 4c46878abcbf5b4d8a7d9f2fd1e1d43e64b29b39..0f89329fe9ffb4dd41cb201b73a194b48d441a11 100644 (file)
@@ -31,8 +31,6 @@ import { DumpStatus, DumpTask } from '../types/project-dump';
 import { TaskStatuses } from '../types/tasks';
 import {
   AlmApplication,
-  Analysis,
-  AnalysisEvent,
   Condition,
   FlowLocation,
   Group,
@@ -43,7 +41,6 @@ import {
   MeasureEnhanced,
   Metric,
   Paging,
-  ParsedAnalysis,
   Period,
   ProfileInheritanceDetails,
   Rule,
@@ -67,52 +64,6 @@ export function mockAlmApplication(overrides: Partial<AlmApplication> = {}): Alm
   };
 }
 
-export function mockAnalysis(overrides: Partial<Analysis> = {}): Analysis {
-  return {
-    date: '2017-03-01T09:36:01+0100',
-    events: [],
-    key: 'foo',
-    projectVersion: '1.0',
-    ...overrides
-  };
-}
-
-export function mockParsedAnalysis(overrides: Partial<ParsedAnalysis> = {}): ParsedAnalysis {
-  return {
-    date: new Date('2017-03-01T09:37:01+0100'),
-    events: [],
-    key: 'foo',
-    projectVersion: '1.0',
-    ...overrides
-  };
-}
-
-export function mockAnalysisEvent(overrides: Partial<AnalysisEvent> = {}): AnalysisEvent {
-  return {
-    category: 'QUALITY_GATE',
-    key: 'E11',
-    description: 'Lorem ipsum dolor sit amet',
-    name: 'Lorem ipsum',
-    qualityGate: {
-      status: 'ERROR',
-      stillFailing: true,
-      failing: [
-        {
-          key: 'foo',
-          name: 'Foo',
-          branch: 'master'
-        },
-        {
-          key: 'bar',
-          name: 'Bar',
-          branch: 'feature/bar'
-        }
-      ]
-    },
-    ...overrides
-  };
-}
-
 export function mockAppState(overrides: Partial<AppState> = {}): AppState {
   return {
     edition: EditionKey.community,
index 065f679d945770af46dcc44b9d4d2d539ee04986..864f9577484581719bbfe00bec47ec7b033b2231 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+interface BaseAnalysis {
+  buildString?: string;
+  detectedCI?: string;
+  events: AnalysisEvent[];
+  key: string;
+  manualNewCodePeriodBaseline?: boolean;
+  projectVersion?: string;
+}
+
+export interface Analysis extends BaseAnalysis {
+  date: string;
+}
+
+export interface ParsedAnalysis extends BaseAnalysis {
+  date: Date;
+}
+
+export interface AnalysisEvent {
+  category: string;
+  description?: string;
+  key: string;
+  name: string;
+  qualityGate?: {
+    failing: Array<{ branch: string; key: string; name: string }>;
+    status: string;
+    stillFailing: boolean;
+  };
+  definitionChange?: {
+    projects: Array<{
+      branch?: string;
+      changeType: string;
+      key: string;
+      name: string;
+      newBranch?: string;
+      oldBranch?: string;
+    }>;
+  };
+}
+
 export enum GraphType {
   issues = 'issues',
   coverage = 'coverage',
index e4495e1bc3c96107089d2e30f9e99cce4ed5e0fa..a6d68a4336c6e2ffe55fee539c09b069f3ef0ea4 100644 (file)
@@ -49,45 +49,6 @@ export interface AlmUnboundApplication {
   name: string;
 }
 
-interface BaseAnalysis {
-  buildString?: string;
-  detectedCI?: string;
-  events: AnalysisEvent[];
-  key: string;
-  manualNewCodePeriodBaseline?: boolean;
-  projectVersion?: string;
-}
-
-export interface Analysis extends BaseAnalysis {
-  date: string;
-}
-
-export interface ParsedAnalysis extends BaseAnalysis {
-  date: Date;
-}
-
-export interface AnalysisEvent {
-  category: string;
-  description?: string;
-  key: string;
-  name: string;
-  qualityGate?: {
-    failing: Array<{ branch: string; key: string; name: string }>;
-    status: string;
-    stillFailing: boolean;
-  };
-  definitionChange?: {
-    projects: Array<{
-      branch?: string;
-      changeType: string;
-      key: string;
-      name: string;
-      newBranch?: string;
-      oldBranch?: string;
-    }>;
-  };
-}
-
 export interface Breadcrumb {
   key: string;
   name: string;