From: Philippe Perrin Date: Thu, 18 Jun 2020 08:24:27 +0000 (+0200) Subject: SONAR-13398 Display an additional message if some tasks are in error X-Git-Tag: 8.4.0.35506~36 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=23d876af1e2b0750cc39bf8113501f303163acac;p=sonarqube.git SONAR-13398 Display an additional message if some tasks are in error --- diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx index e22fb67eeef..4483ae34af0 100644 --- a/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx +++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* eslint-disable react/no-unused-state */ import * as React from 'react'; import { withAppState } from '../../../components/hoc/withAppState'; import { IndexationContextInterface, IndexationStatus } from '../../../types/indexation'; @@ -34,19 +35,13 @@ export class IndexationContextProvider extends React.PureComponent< > { mounted = false; - constructor(props: React.PropsWithChildren) { - super(props); - - this.state = { - status: { isCompleted: !props.appState.needIssueSync } - }; - } - componentDidMount() { this.mounted = true; - if (!this.state.status.isCompleted) { + if (this.props.appState.needIssueSync) { IndexationNotificationHelper.startPolling(this.handleNewStatus); + } else { + this.setState({ status: { isCompleted: true, percentCompleted: 100, hasFailures: false } }); } } 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 e53b6e65257..f83508fb0ca 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 @@ -24,6 +24,7 @@ import withIndexationContext, { WithIndexationContextProps } from '../../../components/hoc/withIndexationContext'; import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users'; +import { IndexationNotificationType } from '../../../types/indexation'; import './IndexationNotification.css'; import IndexationNotificationHelper from './IndexationNotificationHelper'; import IndexationNotificationRenderer from './IndexationNotificationRenderer'; @@ -33,22 +34,16 @@ interface Props extends WithIndexationContextProps { } interface State { - progression?: IndexationProgression; -} - -export enum IndexationProgression { - InProgress, - Completed + notificationType?: IndexationNotificationType; } export class IndexationNotification extends React.PureComponent { - state: State; + state: State = {}; isSystemAdmin = false; constructor(props: Props) { super(props); - this.state = { progression: undefined }; this.isSystemAdmin = isLoggedIn(this.props.currentUser) && hasGlobalPermission(this.props.currentUser, 'admin'); } @@ -57,42 +52,56 @@ export class IndexationNotification extends React.PureComponent { this.refreshNotification(); } - componentDidUpdate() { - this.refreshNotification(); + componentDidUpdate(prevProps: Props) { + if (prevProps.indexationContext.status !== this.props.indexationContext.status) { + this.refreshNotification(); + } } refreshNotification() { - if (!this.props.indexationContext.status.isCompleted) { + const { isCompleted, hasFailures } = this.props.indexationContext.status; + + if (!isCompleted) { IndexationNotificationHelper.markInProgressNotificationAsDisplayed(); - this.setState({ progression: IndexationProgression.InProgress }); + this.setState({ + notificationType: hasFailures + ? IndexationNotificationType.InProgressWithFailure + : IndexationNotificationType.InProgress + }); + } else if (hasFailures) { + this.setState({ notificationType: IndexationNotificationType.CompletedWithFailure }); } else if (IndexationNotificationHelper.shouldDisplayCompletedNotification()) { - this.setState({ progression: IndexationProgression.Completed }); + this.setState({ + notificationType: IndexationNotificationType.Completed + }); + } else { + this.setState({ notificationType: undefined }); } } handleDismissCompletedNotification = () => { - IndexationNotificationHelper.markCompletedNotificationAsDisplayed(); - this.setState({ progression: undefined }); + IndexationNotificationHelper.markCompletedNotificationAsDismissed(); + this.refreshNotification(); }; render() { - const { progression } = this.state; + const { notificationType } = this.state; const { indexationContext: { status: { percentCompleted } } } = this.props; - if (progression === undefined) { + if (notificationType === undefined) { return null; } return ( ); } 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 18e71d12b6c..514e766ce38 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 @@ -31,10 +31,10 @@ export default class IndexationNotificationHelper { static startPolling(onNewStatus: (status: IndexationStatus) => void) { this.stopPolling(); - this.interval = setInterval(async () => { - const status = await getIndexationStatus(); - onNewStatus(status); - }, POLLING_INTERVAL_MS); + // eslint-disable-next-line promise/catch-or-return + this.poll(onNewStatus).finally(() => { + this.interval = setInterval(() => this.poll(onNewStatus), POLLING_INTERVAL_MS); + }); } static stopPolling() { @@ -43,11 +43,17 @@ export default class IndexationNotificationHelper { } } + static async poll(onNewStatus: (status: IndexationStatus) => void) { + const status = await getIndexationStatus(); + + onNewStatus(status); + } + static markInProgressNotificationAsDisplayed() { save(LS_INDEXATION_PROGRESS_WAS_DISPLAYED, true.toString()); } - static markCompletedNotificationAsDisplayed() { + static markCompletedNotificationAsDismissed() { remove(LS_INDEXATION_PROGRESS_WAS_DISPLAYED); } 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 794786e793e..791059c3e90 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 @@ -21,68 +21,143 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { Link } from 'react-router'; -import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import { ClearButton } from 'sonar-ui-common/components/controls/buttons'; +import { Alert, AlertProps } from 'sonar-ui-common/components/ui/Alert'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { BackgroundTaskTypes } from '../../../apps/background-tasks/constants'; -import { IndexationProgression } from './IndexationNotification'; +import { BackgroundTaskTypes, STATUSES } from '../../../apps/background-tasks/constants'; +import { IndexationNotificationType } from '../../../types/indexation'; export interface IndexationNotificationRendererProps { - progression: IndexationProgression; + type: IndexationNotificationType; percentCompleted: number; + isSystemAdmin: boolean; onDismissCompletedNotification: VoidFunction; - displayBackgroundTaskLink?: boolean; } -export default function IndexationNotificationRenderer(props: IndexationNotificationRendererProps) { - const { progression, percentCompleted, displayBackgroundTaskLink } = props; +const NOTIFICATION_VARIANTS: { [key in IndexationNotificationType]: AlertProps['variant'] } = { + [IndexationNotificationType.InProgress]: 'warning', + [IndexationNotificationType.InProgressWithFailure]: 'error', + [IndexationNotificationType.Completed]: 'success', + [IndexationNotificationType.CompletedWithFailure]: 'error' +}; - const inProgress = progression === IndexationProgression.InProgress; +export default function IndexationNotificationRenderer(props: IndexationNotificationRendererProps) { + const { type } = props; return (
+ variant={NOTIFICATION_VARIANTS[type]}>
- {inProgress ? ( - <> - {translate('indexation.in_progress')} - - - {translateWithParameters('indexation.in_progress.details', percentCompleted)} - - {displayBackgroundTaskLink && ( - - - {translate('background_tasks.page')} - - ) - }} - /> - - )} - - ) : ( - <> - {translate('indexation.completed')} - - {translate('dismiss')} - - - )} + {type === IndexationNotificationType.Completed && renderCompletedBanner(props)} + {type === IndexationNotificationType.CompletedWithFailure && + renderCompletedWithFailureBanner(props)} + {type === IndexationNotificationType.InProgress && renderInProgressBanner(props)} + {type === IndexationNotificationType.InProgressWithFailure && + renderInProgressWithFailureBanner(props)}
); } + +function renderCompletedBanner(props: IndexationNotificationRendererProps) { + return ( + <> + {translate('indexation.completed')} + + + ); +} + +function renderCompletedWithFailureBanner(props: IndexationNotificationRendererProps) { + const { isSystemAdmin } = props; + + return ( + + + + ); +} + +function renderInProgressBanner(props: IndexationNotificationRendererProps) { + const { percentCompleted, isSystemAdmin } = props; + + return ( + <> + {translate('indexation.in_progress')} + + + {translateWithParameters('indexation.progression', percentCompleted)} + + {isSystemAdmin && ( + + + + )} + + ); +} + +function renderInProgressWithFailureBanner(props: IndexationNotificationRendererProps) { + const { percentCompleted, isSystemAdmin } = props; + + return ( + <> + {translate('indexation.in_progress')} + + + + + + ); +} + +function renderBackgroundTasksPageLink(hasError: boolean, text: string) { + return ( + + {text} + + ); +} diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx index e0c8fb8130f..d381912d60f 100644 --- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx +++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx @@ -29,26 +29,24 @@ beforeEach(() => jest.clearAllMocks()); jest.mock('../IndexationNotificationHelper'); -it('should render correctly & start polling', () => { +it('should render correctly and start polling if issue sync is needed', () => { const wrapper = mountRender(); - expect(wrapper.state().status).toEqual({ isCompleted: false }); - - const child = wrapper.find(TestComponent); - expect(child.exists()).toBe(true); - expect(child.instance().context).toEqual(wrapper.state()); -}); - -it('should start polling if needed', () => { - mountRender(); - + expect(wrapper).toMatchSnapshot(); expect(IndexationNotificationHelper.startPolling).toHaveBeenCalled(); }); -it('should not start polling if not needed', () => { - mountRender({ appState: { needIssueSync: false } }); +it('should not start polling if no issue sync is needed', () => { + const wrapper = mountRender({ appState: { needIssueSync: false } }); expect(IndexationNotificationHelper.startPolling).not.toHaveBeenCalled(); + + const expectedStatus: IndexationStatus = { + isCompleted: true, + percentCompleted: 100, + hasFailures: false + }; + expect(wrapper.state().status).toEqual(expectedStatus); }); it('should update the state on new status & stop polling if indexation is complete', () => { @@ -56,7 +54,11 @@ it('should update the state on new status & stop polling if indexation is comple const triggerNewStatus = (IndexationNotificationHelper.startPolling as jest.Mock).mock .calls[0][0] as (status: IndexationStatus) => void; - const newStatus = { isCompleted: true, percentCompleted: 100 }; + const newStatus: IndexationStatus = { + isCompleted: true, + percentCompleted: 100, + hasFailures: false + }; triggerNewStatus(newStatus); 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 f09fc87d60a..2efdb483485 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 @@ -21,7 +21,8 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { mockCurrentUser } from '../../../../helpers/testMocks'; -import { IndexationNotification, IndexationProgression } from '../IndexationNotification'; +import { IndexationNotificationType } from '../../../../types/indexation'; +import { IndexationNotification } from '../IndexationNotification'; import IndexationNotificationHelper from '../IndexationNotificationHelper'; import IndexationNotificationRenderer from '../IndexationNotificationRenderer'; @@ -29,63 +30,95 @@ beforeEach(() => jest.clearAllMocks()); jest.mock('../IndexationNotificationHelper'); -it('should display the warning banner if indexation is in progress', () => { - const wrapper = shallowRender(); +describe('Completed banner', () => { + it('should be displayed', () => { + (IndexationNotificationHelper.shouldDisplayCompletedNotification as jest.Mock).mockReturnValueOnce( + true + ); - expect(IndexationNotificationHelper.markInProgressNotificationAsDisplayed).toHaveBeenCalled(); - expect(wrapper.state().progression).toBe(IndexationProgression.InProgress); -}); + const wrapper = shallowRender(); -it('should display the success banner when indexation is complete', () => { - (IndexationNotificationHelper.shouldDisplayCompletedNotification as jest.Mock).mockReturnValueOnce( - true - ); + wrapper.setProps({ + indexationContext: { + status: { isCompleted: true, percentCompleted: 100, hasFailures: false } + } + }); - const wrapper = shallowRender(); + expect(IndexationNotificationHelper.shouldDisplayCompletedNotification).toHaveBeenCalled(); + expect(wrapper.state().notificationType).toBe(IndexationNotificationType.Completed); + }); - wrapper.setProps({ indexationContext: { status: { isCompleted: true } } }); + it('should be displayed at startup', () => { + (IndexationNotificationHelper.shouldDisplayCompletedNotification as jest.Mock).mockReturnValueOnce( + true + ); - expect(IndexationNotificationHelper.shouldDisplayCompletedNotification).toHaveBeenCalled(); - expect(wrapper.state().progression).toBe(IndexationProgression.Completed); -}); + const wrapper = shallowRender({ + indexationContext: { + status: { isCompleted: true, percentCompleted: 100, hasFailures: false } + } + }); -it('should render correctly completed notification at startup', () => { - (IndexationNotificationHelper.shouldDisplayCompletedNotification as jest.Mock).mockReturnValueOnce( - true - ); + expect(IndexationNotificationHelper.shouldDisplayCompletedNotification).toHaveBeenCalled(); + expect(wrapper.state().notificationType).toBe(IndexationNotificationType.Completed); + }); + + it('should be hidden on dismiss action', () => { + (IndexationNotificationHelper.shouldDisplayCompletedNotification as jest.Mock).mockReturnValueOnce( + true + ); + + const wrapper = shallowRender({ + indexationContext: { + status: { isCompleted: true, percentCompleted: 100, hasFailures: false } + } + }); + + expect(wrapper.state().notificationType).toBe(IndexationNotificationType.Completed); + + wrapper + .find(IndexationNotificationRenderer) + .props() + .onDismissCompletedNotification(); + expect(IndexationNotificationHelper.markCompletedNotificationAsDismissed).toHaveBeenCalled(); + expect(wrapper.state().notificationType).toBeUndefined(); + }); +}); + +it('should display the completed-with-failure banner', () => { const wrapper = shallowRender({ - indexationContext: { status: { isCompleted: true } } + indexationContext: { status: { isCompleted: true, percentCompleted: 100, hasFailures: true } } }); - expect(IndexationNotificationHelper.markInProgressNotificationAsDisplayed).not.toHaveBeenCalled(); - expect(IndexationNotificationHelper.shouldDisplayCompletedNotification).toHaveBeenCalled(); - expect(wrapper.state().progression).toBe(IndexationProgression.Completed); + expect(wrapper.state().notificationType).toBe(IndexationNotificationType.CompletedWithFailure); }); -it('should hide the success banner on dismiss action', () => { - (IndexationNotificationHelper.shouldDisplayCompletedNotification as jest.Mock).mockReturnValueOnce( - true - ); - +it('should display the progress banner', () => { const wrapper = shallowRender({ - indexationContext: { status: { isCompleted: true } } + indexationContext: { status: { isCompleted: false, percentCompleted: 23, hasFailures: false } } }); - wrapper - .find(IndexationNotificationRenderer) - .props() - .onDismissCompletedNotification(); + expect(IndexationNotificationHelper.markInProgressNotificationAsDisplayed).toHaveBeenCalled(); + expect(wrapper.state().notificationType).toBe(IndexationNotificationType.InProgress); +}); - expect(IndexationNotificationHelper.markCompletedNotificationAsDisplayed).toHaveBeenCalled(); - expect(wrapper.state().progression).toBeUndefined(); +it('should display the progress-with-failure banner', () => { + const wrapper = shallowRender({ + indexationContext: { status: { isCompleted: false, percentCompleted: 23, hasFailures: true } } + }); + + expect(IndexationNotificationHelper.markInProgressNotificationAsDisplayed).toHaveBeenCalled(); + expect(wrapper.state().notificationType).toBe(IndexationNotificationType.InProgressWithFailure); }); function shallowRender(props?: Partial) { return shallow( ); diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationHelper-test.tsx b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationHelper-test.tsx index 6c3b7363e74..095d4b31948 100644 --- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationHelper-test.tsx +++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationNotificationHelper-test.tsx @@ -40,17 +40,22 @@ jest.mock('sonar-ui-common/helpers/storage', () => ({ it('should properly start & stop polling for indexation status', async () => { const onNewStatus = jest.fn(); - const newStatus: IndexationStatus = { isCompleted: true, percentCompleted: 87 }; + const newStatus: IndexationStatus = { + isCompleted: true, + percentCompleted: 100, + hasFailures: false + }; (getIndexationStatus as jest.Mock).mockResolvedValueOnce(newStatus); IndexationNotificationHelper.startPolling(onNewStatus); - - jest.runOnlyPendingTimers(); expect(getIndexationStatus).toHaveBeenCalled(); await new Promise(setImmediate); expect(onNewStatus).toHaveBeenCalledWith(newStatus); + jest.runOnlyPendingTimers(); + expect(getIndexationStatus).toHaveBeenCalledTimes(2); + (getIndexationStatus as jest.Mock).mockClear(); IndexationNotificationHelper.stopPolling(); @@ -70,7 +75,7 @@ it('should properly handle the flag to show the completed banner', () => { expect(shouldDisplay).toBe(true); expect(get).toHaveBeenCalled(); - IndexationNotificationHelper.markCompletedNotificationAsDisplayed(); + IndexationNotificationHelper.markCompletedNotificationAsDismissed(); expect(remove).toHaveBeenCalled(); 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 ace5c8ee506..ea4f06dbb92 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 @@ -20,37 +20,46 @@ import { shallow } from 'enzyme'; import * as React from 'react'; -import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; +import { ClearButton } from 'sonar-ui-common/components/controls/buttons'; import { click } from 'sonar-ui-common/helpers/testUtils'; -import { IndexationProgression } from '../IndexationNotification'; +import { IndexationNotificationType } from '../../../../types/indexation'; import IndexationNotificationRenderer, { IndexationNotificationRendererProps } from '../IndexationNotificationRenderer'; -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('in-progress'); - expect(shallowRender({ displayBackgroundTaskLink: true })).toMatchSnapshot('in-progress-admin'); - expect(shallowRender({ progression: IndexationProgression.Completed })).toMatchSnapshot( - 'completed' - ); -}); +it.each([ + [IndexationNotificationType.InProgress, false], + [IndexationNotificationType.InProgress, true], + [IndexationNotificationType.InProgressWithFailure, false], + [IndexationNotificationType.InProgressWithFailure, true], + [IndexationNotificationType.Completed, false], + [IndexationNotificationType.Completed, true], + [IndexationNotificationType.CompletedWithFailure, false], + [IndexationNotificationType.CompletedWithFailure, true] +])( + 'should render correctly for type=%p & isSystemAdmin=%p', + (type: IndexationNotificationType, isSystemAdmin: boolean) => { + expect(shallowRender({ type, isSystemAdmin })).toMatchSnapshot(); + } +); -it('should propagate the dismiss event', () => { +it('should propagate the dismiss event from completed notification', () => { const onDismissCompletedNotification = jest.fn(); const wrapper = shallowRender({ - progression: IndexationProgression.Completed, + type: IndexationNotificationType.Completed, onDismissCompletedNotification }); - click(wrapper.find(ButtonLink)); + click(wrapper.find(ClearButton)); expect(onDismissCompletedNotification).toHaveBeenCalled(); }); function shallowRender(props: Partial = {}) { return shallow( diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/PageUnavailableDueToIndexation-test.tsx b/server/sonar-web/src/main/js/app/components/indexation/__tests__/PageUnavailableDueToIndexation-test.tsx index d988c8764dc..9d7f1945d4d 100644 --- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/PageUnavailableDueToIndexation-test.tsx +++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/PageUnavailableDueToIndexation-test.tsx @@ -47,7 +47,7 @@ function shallowRender(props?: PageUnavailableDueToIndexation['props']) { return shallow( + +

+ TestComponent +

+
+ +`; diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationNotificationRenderer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationNotificationRenderer-test.tsx.snap index 2674a8f4a12..8c57b55f88a 100644 --- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationNotificationRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationNotificationRenderer-test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render correctly: completed 1`] = ` +exports[`should render correctly for type="Completed" & isSystemAdmin=false 1`] = `
@@ -12,23 +12,123 @@ exports[`should render correctly: completed 1`] = `
- + indexation.completed - +
+ +
+`; + +exports[`should render correctly for type="Completed" & isSystemAdmin=true 1`] = ` +
+ +
+ + indexation.completed + + +
+
+
+`; + +exports[`should render correctly for type="CompletedWithFailure" & isSystemAdmin=false 1`] = ` +
+ +
+ - - dismiss - - + + +
+
+
+`; + +exports[`should render correctly for type="CompletedWithFailure" & isSystemAdmin=true 1`] = ` +
+ +
+ + + indexation.completed_with_error.link + , + } + } + /> +
`; -exports[`should render correctly: in-progress 1`] = ` +exports[`should render correctly for type="InProgress" & isSystemAdmin=false 1`] = `
@@ -40,23 +140,25 @@ exports[`should render correctly: in-progress 1`] = `
- + indexation.in_progress - indexation.in_progress.details.25 + indexation.progression.25
`; -exports[`should render correctly: in-progress-admin 1`] = ` +exports[`should render correctly for type="InProgress" & isSystemAdmin=true 1`] = `
@@ -68,23 +170,25 @@ exports[`should render correctly: in-progress-admin 1`] = `
- + indexation.in_progress - indexation.in_progress.details.25 + indexation.progression.25
`; + +exports[`should render correctly for type="InProgressWithFailure" & isSystemAdmin=false 1`] = ` +
+ +
+ + indexation.in_progress + + + + + +
+
+
+`; + +exports[`should render correctly for type="InProgressWithFailure" & isSystemAdmin=true 1`] = ` +
+ +
+ + indexation.in_progress + + + + + indexation.progression_with_error.link + , + } + } + /> + +
+
+
+`; diff --git a/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationContext-test.tsx b/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationContext-test.tsx index d4ca9f9452f..3e2a264f5ad 100644 --- a/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationContext-test.tsx +++ b/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationContext-test.tsx @@ -26,7 +26,7 @@ import withIndexationContext, { WithIndexationContextProps } from '../withIndexa it('should render correctly', () => { const indexationContext: IndexationContextInterface = { - status: { isCompleted: true, percentCompleted: 87 } + status: { isCompleted: true, percentCompleted: 87, hasFailures: false } }; const wrapper = mountRender(indexationContext); @@ -36,7 +36,11 @@ it('should render correctly', () => { function mountRender(indexationContext?: Partial) { return mount( - + ); diff --git a/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationGuard-test.tsx b/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationGuard-test.tsx index fca47cf1b8d..c2f4a1bc42a 100644 --- a/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationGuard-test.tsx +++ b/server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationGuard-test.tsx @@ -29,13 +29,19 @@ it('should render correctly', () => { let wrapper = mountRender(); expect(wrapper.find(TestComponent).exists()).toBe(false); - wrapper = mountRender({ status: { isCompleted: true } }); + wrapper = mountRender({ + status: { isCompleted: true, percentCompleted: 100, hasFailures: false } + }); expect(wrapper.find(TestComponent).exists()).toBe(true); }); function mountRender(context?: Partial) { return mount( - + ); diff --git a/server/sonar-web/src/main/js/types/indexation.ts b/server/sonar-web/src/main/js/types/indexation.ts index 28f9b6eba53..7ec70554bd1 100644 --- a/server/sonar-web/src/main/js/types/indexation.ts +++ b/server/sonar-web/src/main/js/types/indexation.ts @@ -20,9 +20,17 @@ export interface IndexationStatus { isCompleted: boolean; - percentCompleted?: number; + percentCompleted: number; + hasFailures: boolean; } export interface IndexationContextInterface { status: IndexationStatus; } + +export enum IndexationNotificationType { + InProgress = 'InProgress', + InProgressWithFailure = 'InProgressWithFailure', + Completed = 'Completed', + CompletedWithFailure = 'CompletedWithFailure' +} 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 97bd3af3747..5aafe022028 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3563,9 +3563,13 @@ maintenance.sonarqube_is_offline.text=The connection to SonarQube is lost. Pleas # #------------------------------------------------------------------------------ indexation.in_progress=SonarQube is reloading project data. Some projects will be unavailable until this process is complete. -indexation.in_progress.details={0}% completed. -indexation.in_progress.admin_details=See {link}. +indexation.progression={0}% complete. +indexation.progression_with_error={0}% complete with some {link}. +indexation.progression_with_error.link=tasks failing indexation.completed=All project data has been reloaded. +indexation.completed_with_error=SonarQube completed the reload of project data. Some {link} causing some projects to remain unavailable. +indexation.completed_with_error.link=tasks failed +indexation.admin_link=See {link} for more information. indexation.page_unavailable.title.issues=Issues page is temporarily unavailable indexation.page_unavailable.title.portfolios=Portfolios page is temporarily unavailable indexation.page_unavailable.title={componentQualifier} {componentName} is temporarily unavailable