aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx61
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationHelper.ts9
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx199
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx96
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationRenderer-test.tsx10
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties3
6 files changed, 282 insertions, 96 deletions
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx
index 84444f6878b..00e1fb09675 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotification.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { isBefore } from 'date-fns';
import * as React from 'react';
import withIndexationContext, {
WithIndexationContextProps,
@@ -26,6 +27,7 @@ import { hasGlobalPermission } from '../../../helpers/users';
import { IndexationNotificationType } from '../../../types/indexation';
import { Permissions } from '../../../types/permissions';
import { CurrentUser, isLoggedIn } from '../../../types/users';
+import withAppStateContext, { WithAppStateContextProps } from '../app-state/withAppStateContext';
import withCurrentUserContext from '../current-user/withCurrentUserContext';
import IndexationNotificationHelper from './IndexationNotificationHelper';
import IndexationNotificationRenderer from './IndexationNotificationRenderer';
@@ -36,15 +38,24 @@ interface Props extends WithIndexationContextProps {
interface State {
notificationType?: IndexationNotificationType;
+ shouldDisplaySurveyLink: boolean;
}
-const COMPLETED_NOTIFICATION_DISPLAY_DURATION = 5000;
+type IndexationNotificationProps = Props & WithIndexationContextProps & WithAppStateContextProps;
+
+const SPRIG_SURVEY_LIMIT_DATE = new Date('2025-07-01T00:00:00+01:00');
+
+export class IndexationNotification extends React.PureComponent<
+ IndexationNotificationProps,
+ State
+> {
+ state: State = {
+ shouldDisplaySurveyLink: false,
+ };
-export class IndexationNotification extends React.PureComponent<Props, State> {
- state: State = {};
isSystemAdmin = false;
- constructor(props: Props) {
+ constructor(props: IndexationNotificationProps) {
super(props);
this.isSystemAdmin =
@@ -62,9 +73,20 @@ export class IndexationNotification extends React.PureComponent<Props, State> {
}
}
+ dismissBanner = () => {
+ this.setState({ notificationType: undefined });
+ };
+
refreshNotification() {
const { isCompleted, hasFailures } = this.props.indexationContext.status;
+ const currentSqsVersion = this.props.appState.version;
+ this.setState({
+ shouldDisplaySurveyLink:
+ isBefore(new Date(), SPRIG_SURVEY_LIMIT_DATE) &&
+ IndexationNotificationHelper.getLastIndexationSQSVersion() !== currentSqsVersion,
+ });
+
if (!isCompleted) {
IndexationNotificationHelper.markCompletedNotificationAsToDisplay();
@@ -73,26 +95,31 @@ export class IndexationNotification extends React.PureComponent<Props, State> {
? IndexationNotificationType.InProgressWithFailure
: IndexationNotificationType.InProgress,
});
- } else if (hasFailures) {
+
+ return;
+ }
+
+ IndexationNotificationHelper.saveLastIndexationSQSVersion(this.props.appState.version);
+
+ if (hasFailures) {
this.setState({ notificationType: IndexationNotificationType.CompletedWithFailure });
- } else if (IndexationNotificationHelper.shouldDisplayCompletedNotification()) {
+ return;
+ }
+
+ if (IndexationNotificationHelper.shouldDisplayCompletedNotification()) {
this.setState({
notificationType: IndexationNotificationType.Completed,
});
IndexationNotificationHelper.markCompletedNotificationAsDisplayed();
-
- // Hide after some time
- setTimeout(() => {
- this.refreshNotification();
- }, COMPLETED_NOTIFICATION_DISPLAY_DURATION);
- } else {
- this.setState({ notificationType: undefined });
+ return;
}
+
+ this.setState({ notificationType: undefined });
}
render() {
- const { notificationType } = this.state;
+ const { notificationType, shouldDisplaySurveyLink } = this.state;
const {
indexationContext: {
@@ -103,6 +130,8 @@ export class IndexationNotification extends React.PureComponent<Props, State> {
return !this.isSystemAdmin ? null : (
<IndexationNotificationRenderer
completedCount={completedCount}
+ onDismissBanner={this.dismissBanner}
+ shouldDisplaySurveyLink={shouldDisplaySurveyLink}
total={total}
type={notificationType}
/>
@@ -110,4 +139,6 @@ export class IndexationNotification extends React.PureComponent<Props, State> {
}
}
-export default withCurrentUserContext(withIndexationContext(IndexationNotification));
+export default withCurrentUserContext(
+ withIndexationContext(withAppStateContext(IndexationNotification)),
+);
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationHelper.ts b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationHelper.ts
index 41211329fd7..ffe785edb03 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationHelper.ts
+++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationHelper.ts
@@ -25,6 +25,7 @@ import { IndexationStatus } from '../../../types/indexation';
const POLLING_INTERVAL_MS = 5000;
const LS_INDEXATION_COMPLETED_NOTIFICATION_SHOULD_BE_DISPLAYED =
'display_indexation_completed_notification';
+const LS_LAST_INDEXATION_SQS_VERSION = 'last_indexation_sqs_version';
export default class IndexationNotificationHelper {
private static interval?: NodeJS.Timeout;
@@ -74,4 +75,12 @@ export default class IndexationNotificationHelper {
get(LS_INDEXATION_COMPLETED_NOTIFICATION_SHOULD_BE_DISPLAYED) ?? false.toString(),
);
}
+
+ static saveLastIndexationSQSVersion(version: string) {
+ save(LS_LAST_INDEXATION_SQS_VERSION, version);
+ }
+
+ static getLastIndexationSQSVersion() {
+ return get(LS_LAST_INDEXATION_SQS_VERSION);
+ }
}
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
index be82875356e..2bddeadc942 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
@@ -20,26 +20,28 @@
/* eslint-disable react/no-unused-prop-types */
import styled from '@emotion/styled';
-import { FormattedMessage } from 'react-intl';
import {
- FlagErrorIcon,
- FlagSuccessIcon,
- FlagWarningIcon,
+ IconCheckCircle,
+ IconError,
+ IconWarning,
+ IconX,
Link,
+ LinkHighlight,
Spinner,
- ThemeColors,
- themeBorder,
- themeColor,
-} from '~design-system';
+} from '@sonarsource/echoes-react';
+import { FormattedMessage, useIntl } from 'react-intl';
+import { InteractiveIconBase, ThemeColors, themeBorder, themeColor } from '~design-system';
import { queryToSearchString } from '~sonar-aligned/helpers/urls';
import DocumentationLink from '../../../components/common/DocumentationLink';
import { DocLink } from '../../../helpers/doc-links';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { IndexationNotificationType } from '../../../types/indexation';
import { TaskStatuses, TaskTypes } from '../../../types/tasks';
-export interface IndexationNotificationRendererProps {
+interface IndexationNotificationRendererProps {
completedCount?: number;
+ onDismissBanner: () => void;
+ shouldDisplaySurveyLink: boolean;
total?: number;
type?: IndexationNotificationType;
}
@@ -65,8 +67,13 @@ const NOTIFICATION_COLORS: {
},
};
-export default function IndexationNotificationRenderer(props: IndexationNotificationRendererProps) {
- const { completedCount, total, type } = props;
+const SPRIG_SURVEY_LINK =
+ 'https://a.sprig.com/U1h4UFpySUNwN2ZtfnNpZDowNWUyNmRkZC01MmUyLTQ4OGItOTA3ZC05M2VjYjQxZTYzN2Y=';
+
+export default function IndexationNotificationRenderer(
+ props: Readonly<IndexationNotificationRendererProps>,
+) {
+ const { type } = props;
return (
<div className={type === undefined ? 'sw-hidden' : ''}>
@@ -76,62 +83,126 @@ export default function IndexationNotificationRenderer(props: IndexationNotifica
aria-live="assertive"
role="alert"
>
- {type !== undefined && (
- <>
- {renderIcon(type)}
- {type === IndexationNotificationType.Completed && renderCompletedBanner()}
-
- {type === IndexationNotificationType.CompletedWithFailure &&
- renderCompletedWithFailureBanner()}
-
- {type === IndexationNotificationType.InProgress &&
- renderInProgressBanner(completedCount as number, total as number)}
-
- {type === IndexationNotificationType.InProgressWithFailure &&
- renderInProgressWithFailureBanner(completedCount as number, total as number)}
- </>
- )}
+ <IndexationStatusIcon type={type} />
+ <IndexationBanner {...props} />
</StyledBanner>
</div>
);
}
-function renderIcon(type: IndexationNotificationType) {
+function IndexationStatusIcon(props: Readonly<{ type?: IndexationNotificationType }>) {
+ const { type } = props;
+
switch (type) {
case IndexationNotificationType.Completed:
- return <FlagSuccessIcon />;
+ return <IconCheckCircle color="echoes-color-icon-success" />;
case IndexationNotificationType.CompletedWithFailure:
case IndexationNotificationType.InProgressWithFailure:
- return <FlagErrorIcon />;
+ return <IconError color="echoes-color-icon-danger" />;
case IndexationNotificationType.InProgress:
- return <FlagWarningIcon />;
+ return <IconWarning color="echoes-color-icon-warning" />;
default:
return null;
}
}
-function renderCompletedBanner() {
- return <span>{translate('indexation.completed')}</span>;
+function IndexationBanner(props: Readonly<IndexationNotificationRendererProps>) {
+ const { completedCount, onDismissBanner, shouldDisplaySurveyLink, total, type } = props;
+
+ switch (type) {
+ case IndexationNotificationType.Completed:
+ return (
+ <CompletedBanner
+ onDismissBanner={onDismissBanner}
+ shouldDisplaySurveyLink={shouldDisplaySurveyLink}
+ />
+ );
+ case IndexationNotificationType.CompletedWithFailure:
+ return <CompletedWithFailureBanner shouldDisplaySurveyLink={shouldDisplaySurveyLink} />;
+ case IndexationNotificationType.InProgress:
+ return <InProgressBanner completedCount={completedCount as number} total={total as number} />;
+ case IndexationNotificationType.InProgressWithFailure:
+ return (
+ <InProgressWithFailureBanner
+ completedCount={completedCount as number}
+ total={total as number}
+ />
+ );
+ default:
+ return null;
+ }
+}
+
+function SurveyLink() {
+ return (
+ <span className="sw-ml-2">
+ <FormattedMessage
+ id="indexation.upgrade_survey_link"
+ values={{
+ link: (text) => (
+ <Link highlight={LinkHighlight.Default} shouldOpenInNewTab to={SPRIG_SURVEY_LINK}>
+ {text}
+ </Link>
+ ),
+ }}
+ />
+ </span>
+ );
+}
+
+function CompletedBanner(
+ props: Readonly<{ onDismissBanner: () => void; shouldDisplaySurveyLink: boolean }>,
+) {
+ const { onDismissBanner, shouldDisplaySurveyLink } = props;
+
+ const intl = useIntl();
+
+ return (
+ <div className="sw-flex sw-flex-1 sw-items-center">
+ <FormattedMessage id="indexation.completed" />
+ {shouldDisplaySurveyLink && <SurveyLink />}
+ <div className="sw-flex sw-flex-1 sw-justify-end">
+ <BannerDismissIcon
+ className="sw-ml-2 sw-px-1/2"
+ Icon={IconX}
+ aria-label={intl.formatMessage({ id: 'dismiss' })}
+ onClick={onDismissBanner}
+ size="small"
+ />
+ </div>
+ </div>
+ );
}
-function renderCompletedWithFailureBanner() {
+function CompletedWithFailureBanner(props: Readonly<{ shouldDisplaySurveyLink: boolean }>) {
+ const { shouldDisplaySurveyLink } = props;
+
+ const { formatMessage } = useIntl();
+
return (
<span>
<FormattedMessage
defaultMessage={translate('indexation.completed_with_error')}
id="indexation.completed_with_error"
values={{
- link: renderBackgroundTasksPageLink(
- true,
- translate('indexation.completed_with_error.link'),
+ link: (
+ <BackgroundTasksPageLink
+ hasError
+ text={formatMessage({ id: 'indexation.completed_with_error.link' })}
+ />
),
}}
/>
+ {shouldDisplaySurveyLink && <SurveyLink />}
</span>
);
}
-function renderInProgressBanner(completedCount: number, total: number) {
+function InProgressBanner(props: Readonly<{ completedCount: number; total: number }>) {
+ const { completedCount, total } = props;
+
+ const { formatMessage } = useIntl();
+
return (
<>
<span>
@@ -139,25 +210,32 @@ function renderInProgressBanner(completedCount: number, total: number) {
<FormattedMessage
id="indexation.features_partly_available"
values={{
- link: renderIndexationDocPageLink(),
+ link: <IndexationDocPageLink />,
}}
/>
</span>
<span className="sw-flex sw-items-center">
<Spinner className="sw-mr-1 -sw-mb-1/2" />
- {translateWithParameters(
- 'indexation.progression',
- completedCount.toString(),
- total.toString(),
- )}
+ <FormattedMessage
+ id="indexation.progression"
+ values={{
+ count: completedCount,
+ total,
+ }}
+ />
</span>
<span>
<FormattedMessage
id="indexation.admin_link"
values={{
- link: renderBackgroundTasksPageLink(false, translate('background_tasks.page')),
+ link: (
+ <BackgroundTasksPageLink
+ hasError={false}
+ text={formatMessage({ id: 'background_tasks.page' })}
+ />
+ ),
}}
/>
</span>
@@ -165,7 +243,11 @@ function renderInProgressBanner(completedCount: number, total: number) {
);
}
-function renderInProgressWithFailureBanner(completedCount: number, total: number) {
+function InProgressWithFailureBanner(props: Readonly<{ completedCount: number; total: number }>) {
+ const { completedCount, total } = props;
+
+ const { formatMessage } = useIntl();
+
return (
<>
<span>
@@ -173,7 +255,7 @@ function renderInProgressWithFailureBanner(completedCount: number, total: number
<FormattedMessage
id="indexation.features_partly_available"
values={{
- link: renderIndexationDocPageLink(),
+ link: <IndexationDocPageLink />,
}}
/>
</span>
@@ -186,9 +268,11 @@ function renderInProgressWithFailureBanner(completedCount: number, total: number
values={{
count: completedCount,
total,
- link: renderBackgroundTasksPageLink(
- true,
- translate('indexation.progression_with_error.link'),
+ link: (
+ <BackgroundTasksPageLink
+ hasError
+ text={formatMessage({ id: 'indexation.progression_with_error.link' })}
+ />
),
}}
/>
@@ -197,7 +281,9 @@ function renderInProgressWithFailureBanner(completedCount: number, total: number
);
}
-function renderBackgroundTasksPageLink(hasError: boolean, text: string) {
+function BackgroundTasksPageLink(props: Readonly<{ hasError: boolean; text: string }>) {
+ const { hasError, text } = props;
+
return (
<Link
to={{
@@ -213,7 +299,7 @@ function renderBackgroundTasksPageLink(hasError: boolean, text: string) {
);
}
-function renderIndexationDocPageLink() {
+function IndexationDocPageLink() {
return (
<DocumentationLink className="sw-whitespace-nowrap" to={DocLink.InstanceAdminReindexation}>
<FormattedMessage id="indexation.features_partly_available.link" />
@@ -230,3 +316,12 @@ const StyledBanner = styled.div<{ type: IndexationNotificationType }>`
background-color: ${({ type }) => themeColor(NOTIFICATION_COLORS[type].background)};
border-bottom: ${({ type }) => themeBorder('default', NOTIFICATION_COLORS[type].border)};
`;
+
+// There's currently no banner in Echoes so let's use the legacy design-system components for now
+const BannerDismissIcon = styled(InteractiveIconBase)`
+ --background: ${themeColor('successBackground')};
+ --backgroundHover: ${themeColor('successText', 0.1)};
+ --color: ${themeColor('successText')};
+ --colorHover: ${themeColor('successText')};
+ --focus: ${themeColor('bannerIconFocus', 0.2)};
+`;
diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx
index 07cb968d520..ab005ef0d4a 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotification-test.tsx
@@ -18,9 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { act } from '@testing-library/react';
-import { byText } from '~sonar-aligned/helpers/testSelector';
-import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
+import { byRole, byText } from '~sonar-aligned/helpers/testSelector';
+import { mockAppState, mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
import { Permissions } from '../../../../types/permissions';
import { IndexationNotification } from '../IndexationNotification';
@@ -40,6 +39,7 @@ describe('Completed banner', () => {
rerender(
<IndexationNotification
+ appState={mockAppState()}
currentUser={mockCurrentUser()}
indexationContext={{
status: { completedCount: 23, hasFailures: false, isCompleted: true, total: 42 },
@@ -64,77 +64,118 @@ describe('Completed banner', () => {
expect(IndexationNotificationHelper.shouldDisplayCompletedNotification).toHaveBeenCalled();
});
- it('should be hidden once completed without failure', () => {
- jest.useFakeTimers();
-
- jest
- .mocked(IndexationNotificationHelper.shouldDisplayCompletedNotification)
- .mockReturnValueOnce(true);
-
- renderIndexationNotification({
+ it('should start progress > progress with failure > complete with failure', () => {
+ const { rerender } = renderIndexationNotification({
indexationContext: {
- status: { hasFailures: false, isCompleted: true },
+ status: { completedCount: 23, hasFailures: false, isCompleted: false, total: 42 },
},
});
- expect(IndexationNotificationHelper.markCompletedNotificationAsDisplayed).toHaveBeenCalled();
+ expect(byText('indexation.progression2342').get()).toBeInTheDocument();
- act(() => jest.runOnlyPendingTimers());
+ rerender(
+ <IndexationNotification
+ appState={mockAppState()}
+ currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })}
+ indexationContext={{
+ status: { completedCount: 23, hasFailures: true, isCompleted: false, total: 42 },
+ }}
+ />,
+ );
- expect(IndexationNotificationHelper.markCompletedNotificationAsDisplayed).toHaveBeenCalled();
+ expect(byText(/^indexation\.progression_with_error\.link/).get()).toBeInTheDocument();
- jest.useRealTimers();
+ rerender(
+ <IndexationNotification
+ appState={mockAppState()}
+ currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })}
+ indexationContext={{
+ status: { completedCount: 23, hasFailures: true, isCompleted: true, total: 42 },
+ }}
+ />,
+ );
+ expect(byText('indexation.completed_with_error').get()).toBeInTheDocument();
});
- it('should start progress > progress with failure > complete with failure', () => {
+ it('should start progress > success > disappear', () => {
const { rerender } = renderIndexationNotification({
indexationContext: {
status: { completedCount: 23, hasFailures: false, isCompleted: false, total: 42 },
},
});
- expect(byText('indexation.progression.23.42').get()).toBeInTheDocument();
+ expect(byText('indexation.progression2342').get()).toBeInTheDocument();
rerender(
<IndexationNotification
+ appState={mockAppState()}
currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })}
indexationContext={{
- status: { completedCount: 23, hasFailures: true, isCompleted: false, total: 42 },
+ status: { completedCount: 23, hasFailures: false, isCompleted: true, total: 42 },
}}
/>,
);
+ expect(IndexationNotificationHelper.shouldDisplayCompletedNotification).toHaveBeenCalled();
+ });
- expect(byText(/^indexation\.progression_with_error\.link/).get()).toBeInTheDocument();
+ it('should show survey link when indexation follows an upgrade', () => {
+ jest
+ .mocked(IndexationNotificationHelper.shouldDisplayCompletedNotification)
+ .mockReturnValueOnce(true);
+ jest
+ .mocked(IndexationNotificationHelper.getLastIndexationSQSVersion)
+ .mockReturnValueOnce('11.0');
+
+ const { rerender } = renderIndexationNotification({
+ appState: mockAppState({ version: '12.0' }),
+ indexationContext: {
+ status: { completedCount: 42, hasFailures: false, isCompleted: true, total: 42 },
+ },
+ });
+
+ expect(byText('indexation.upgrade_survey_link').get()).toBeInTheDocument();
rerender(
<IndexationNotification
+ appState={mockAppState({ version: '12.0' })}
currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })}
indexationContext={{
status: { completedCount: 23, hasFailures: true, isCompleted: true, total: 42 },
}}
/>,
);
- expect(byText('indexation.completed_with_error').get()).toBeInTheDocument();
+
+ expect(byText('indexation.upgrade_survey_link').get()).toBeInTheDocument();
});
- it('should start progress > success > disappear', () => {
+ it('should not show survey link when indexation does not follow an upgrade', () => {
+ jest
+ .mocked(IndexationNotificationHelper.shouldDisplayCompletedNotification)
+ .mockReturnValueOnce(true);
+ jest
+ .mocked(IndexationNotificationHelper.getLastIndexationSQSVersion)
+ .mockReturnValueOnce('12.0');
+
const { rerender } = renderIndexationNotification({
+ appState: mockAppState({ version: '12.0' }),
indexationContext: {
- status: { completedCount: 23, hasFailures: false, isCompleted: false, total: 42 },
+ status: { completedCount: 42, hasFailures: false, isCompleted: true, total: 42 },
},
});
- expect(byText('indexation.progression.23.42').get()).toBeInTheDocument();
+ expect(byRole('indexation.upgrade_survey_link').query()).not.toBeInTheDocument();
rerender(
<IndexationNotification
+ appState={mockAppState({ version: '12.0' })}
currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })}
indexationContext={{
- status: { completedCount: 23, hasFailures: false, isCompleted: true, total: 42 },
+ status: { completedCount: 23, hasFailures: true, isCompleted: true, total: 42 },
}}
/>,
);
- expect(IndexationNotificationHelper.shouldDisplayCompletedNotification).toHaveBeenCalled();
+
+ expect(byRole('indexation.upgrade_survey_link').query()).not.toBeInTheDocument();
});
it('should not see notification if not admin', () => {
@@ -145,13 +186,14 @@ describe('Completed banner', () => {
currentUser: mockLoggedInUser(),
});
- expect(byText('indexation.progression.23.42').query()).not.toBeInTheDocument();
+ expect(byText('indexation.progression2342').query()).not.toBeInTheDocument();
});
});
function renderIndexationNotification(props?: Partial<IndexationNotification['props']>) {
return renderComponent(
<IndexationNotification
+ appState={mockAppState()}
currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })}
indexationContext={{
status: { completedCount: 23, hasFailures: false, isCompleted: false, total: 42 },
diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationRenderer-test.tsx b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationRenderer-test.tsx
index b9c958ec7ab..e57085f14b2 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationRenderer-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationRenderer-test.tsx
@@ -59,5 +59,13 @@ describe('Indexation notification renderer', () => {
});
function renderIndexationNotificationRenderer(status: IndexationNotificationType) {
- renderComponent(<IndexationNotificationRenderer completedCount={23} total={42} type={status} />);
+ renderComponent(
+ <IndexationNotificationRenderer
+ completedCount={23}
+ onDismissBanner={() => undefined}
+ shouldDisplaySurveyLink={false}
+ total={42}
+ type={status}
+ />,
+ );
}
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index ba23172b611..21d5616bea8 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -5789,7 +5789,7 @@ indexation.in_progress=Reindexing in progress.
indexation.details_unavailable=Details are unavailable until this process is complete.
indexation.features_partly_available=Most features are available. Some details only show upon completion. {link}
indexation.features_partly_available.link=More info
-indexation.progression={0} out of {1} projects reindexed.
+indexation.progression={count} out of {total} projects reindexed.
indexation.progression_with_error={count} out of {total} projects reindexed with some {link}.
indexation.progression_with_error.link=tasks failing
indexation.completed=All project data has been reloaded.
@@ -5805,6 +5805,7 @@ indexation.filter_unavailable.description=This filter is unavailable until this
indexation.learn_more=Learn more:
indexation.reindexing=Reindexing
indexation.filters_unavailable=Some filters are unavailable until this process is complete. {link}
+indexation.upgrade_survey_link=Help us improve the upgrade experience. <link>Click here to share your thoughts.</link>
#------------------------------------------------------------------------------