import { FormattedMessage } from 'react-intl';
import { isDefinitionChangeEvent } from '../../../components/activity-graph/DefinitionChangeEventInner';
import { isRichQualityGateEvent } from '../../../components/activity-graph/RichQualityGateEventInner';
+import {
+ RichQualityProfileEventInner,
+ isRichQualityProfileEvent,
+} from '../../../components/activity-graph/RichQualityProfileEventInner';
import { SqUpgradeActivityEventMessage } from '../../../components/activity-graph/SqUpgradeActivityEventMessage';
import { translate } from '../../../helpers/l10n';
import { AnalysisEvent, ProjectAnalysisEventCategory } from '../../../types/project-activity';
return <SqUpgradeActivityEventMessage event={event} />;
}
+ if (isRichQualityProfileEvent(event)) {
+ return <RichQualityProfileEventInner event={event} />;
+ }
+
const eventCategory = translate('event.category', event.category);
if (isDefinitionChangeEvent(event)) {
return <div className="sw-mb-1">{eventCategory}</div>;
} from '../../../../helpers/mocks/project-activity';
import { mockMetric } from '../../../../helpers/testMocks';
+import userEvent from '@testing-library/user-event';
+import { Route, useSearchParams } from 'react-router-dom';
import { parseDate } from '../../../../helpers/dates';
-import { renderComponent } from '../../../../helpers/testReactTestingUtils';
+import { renderAppRoutes } from '../../../../helpers/testReactTestingUtils';
import { MetricKey } from '../../../../types/metrics';
import {
ApplicationAnalysisEventCategory,
import ActivityPanel, { ActivityPanelProps } from '../ActivityPanel';
it('should render correctly', async () => {
+ const user = userEvent.setup();
renderActivityPanel();
+
expect(await screen.findAllByText('metric.level.ERROR')).toHaveLength(2);
expect(screen.getAllByText('metric.level.OK')).toHaveLength(2);
expect(screen.getByRole('status', { name: 'v1.0' })).toBeInTheDocument();
expect(screen.getByText(/^502 project_activity\.graphs\.issues$/)).toBeInTheDocument();
expect(screen.getByText(/^0\.0% project_activity\.graphs\.coverage$/)).toBeInTheDocument();
expect(screen.getByText(/^10\.0% project_activity\.graphs\.duplications$/)).toBeInTheDocument();
+
+ // Rich Quality Profile event
+ expect(
+ screen.getByLabelText(
+ /Quality profile QP-test has been updated with 1 new rule, 2 modified rules, and 3 removed rules/,
+ ),
+ ).toBeInTheDocument();
+
+ await user.click(
+ screen.getByRole('link', { name: 'quality_profiles.page_title_changelog_x.QP-test' }),
+ );
+
+ expect(await screen.findByText('QP-test java')).toBeInTheDocument();
});
-function renderActivityPanel(props: Partial<ActivityPanelProps> = {}) {
+function renderActivityPanel() {
const mockedMeasureHistory = [
mockMeasureHistory({
metric: MetricKey.code_smells,
category: ProjectAnalysisEventCategory.SqUpgrade,
name: '10.2',
}),
+ mockAnalysisEvent({
+ key: '6',
+ category: ProjectAnalysisEventCategory.QualityProfile,
+ name: 'Quality profile QP-test has been updated with 1 new rule, 2 modified rules, and 3 removed rules',
+ description: '1 new rule, 2 modified rules, and 3 removed rules',
+ qualityProfile: {
+ languageKey: 'java',
+ name: 'QP-test',
+ key: 'testkey',
+ },
+ }),
],
}),
mockAnalysis({ key: 'bar' }),
loading: false,
};
- return renderComponent(<ActivityPanel {...mockedProps} {...props} />);
+ return renderAppRoutes('overview', () => (
+ <>
+ <Route path="/overview" element={<ActivityPanel {...mockedProps} />} />
+ <Route
+ path="/profiles/changelog"
+ Component={() => {
+ const [searchParams] = useSearchParams();
+ return (
+ <div>
+ {searchParams.get('name')} {searchParams.get('language')}
+ </div>
+ );
+ }}
+ />
+ </>
+ ));
}
import Tooltip from '../controls/Tooltip';
import { DefinitionChangeEventInner, isDefinitionChangeEvent } from './DefinitionChangeEventInner';
import { RichQualityGateEventInner, isRichQualityGateEvent } from './RichQualityGateEventInner';
+import {
+ RichQualityProfileEventInner,
+ isRichQualityProfileEvent,
+} from './RichQualityProfileEventInner';
import { SqUpgradeActivityEventMessage } from './SqUpgradeActivityEventMessage';
export interface EventInnerProps {
return <RichQualityGateEventInner event={event} readonly={readonly} />;
} else if (isDefinitionChangeEvent(event)) {
return <DefinitionChangeEventInner branchLike={branchLike} event={event} readonly={readonly} />;
+ } else if (isRichQualityProfileEvent(event)) {
+ return <RichQualityProfileEventInner event={event} />;
} else if (event.category === ProjectAnalysisEventCategory.SqUpgrade) {
return <SqUpgradeActivityEventMessage event={event} />;
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 { Link } from 'design-system';
+import React from 'react';
+import { useIntl } from 'react-intl';
+import { getProfileChangelogPath } from '../../apps/quality-profiles/utils';
+import { AnalysisEvent, ProjectAnalysisEventCategory } from '../../types/project-activity';
+
+type RichQualityGateEvent = AnalysisEvent &
+ Required<Pick<AnalysisEvent, 'description' | 'qualityProfile'>>;
+
+interface RichQualityProfileEventInnerProps {
+ event: RichQualityGateEvent;
+}
+
+export function isRichQualityProfileEvent(event: AnalysisEvent): event is RichQualityGateEvent {
+ return (
+ event.category === ProjectAnalysisEventCategory.QualityProfile &&
+ event.description !== undefined &&
+ event.qualityProfile !== undefined
+ );
+}
+
+export function RichQualityProfileEventInner({
+ event,
+}: Readonly<RichQualityProfileEventInnerProps>) {
+ const {
+ description,
+ name,
+ qualityProfile: { languageKey, name: qualityProfileName },
+ } = event;
+ const intl = useIntl();
+
+ const truncatedName = name.split(description)[0];
+
+ return (
+ <span aria-label={name}>
+ {truncatedName}
+ <Link
+ aria-label={intl.formatMessage(
+ { id: 'quality_profiles.page_title_changelog_x' },
+ { 0: qualityProfileName },
+ )}
+ to={getProfileChangelogPath(qualityProfileName, languageKey)}
+ >
+ {description}
+ </Link>
+ </span>
+ );
+}
oldBranch?: string;
}>;
};
+ qualityProfile?: {
+ key: string;
+ languageKey: string;
+ name: string;
+ };
}
export enum GraphType {