aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorDavid Cho-Lerat <david.cho-lerat@sonarsource.com>2024-11-27 09:11:58 +0100
committersonartech <sonartech@sonarsource.com>2024-11-29 20:03:09 +0000
commit5f497026206e3f7b47389d1bac0abc89a1d2c6ca (patch)
tree4a8bb7898aa027a0f3873719890b51d0d6857500 /server
parent692dcd4fe7c59fed8d379d1aec50430fac4139e5 (diff)
downloadsonarqube-5f497026206e3f7b47389d1bac0abc89a1d2c6ca.tar.gz
sonarqube-5f497026206e3f7b47389d1bac0abc89a1d2c6ca.zip
SONAR-23803 show upgrade banner(s) and modals for SQCB / SQS
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/app/components/GlobalContainer.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/UpdateNotification-it.tsx909
-rw-r--r--server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx4
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/SQCBUpdateBanners.tsx128
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/SQSUpdateBanner.tsx99
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx97
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/helpers.ts57
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx24
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx59
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx82
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/design-system/sonar-aligned/components/buttons/ButtonSecondary.tsx2
-rw-r--r--server/sonar-web/src/main/js/helpers/l10nBundle.ts5
-rw-r--r--server/sonar-web/src/main/js/helpers/mocks/system-upgrades.ts16
-rw-r--r--server/sonar-web/src/main/js/types/system.ts11
18 files changed, 978 insertions, 528 deletions
diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
index bb12f43c094..ef1e27b7946 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
@@ -38,7 +38,7 @@ import GlobalNav from './nav/global/GlobalNav';
import PromotionNotification from './promotion-notification/PromotionNotification';
import StartupModal from './StartupModal';
import SystemAnnouncement from './SystemAnnouncement';
-import UpdateNotification from './update-notification/UpdateNotification';
+import { UpdateNotification } from './update-notification/UpdateNotification';
/*
* These pages need a white background (aka 'secondary', rather than the default 'primary')
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/UpdateNotification-it.tsx b/server/sonar-web/src/main/js/app/components/__tests__/UpdateNotification-it.tsx
index 9082fd8791f..6ef0cf9ece1 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/UpdateNotification-it.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/UpdateNotification-it.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { addDays, formatISO, subDays } from 'date-fns';
import { byRole } from '~sonar-aligned/helpers/testSelector';
@@ -26,445 +27,587 @@ import { UpdateUseCase } from '../../../components/upgrade/utils';
import { mockAppState, mockCurrentUser } from '../../../helpers/testMocks';
import { renderComponent } from '../../../helpers/testReactTestingUtils';
import { AppState } from '../../../types/appstate';
+import { EditionKey } from '../../../types/editions';
import { Permissions } from '../../../types/permissions';
+import { ProductNameForUpgrade } from '../../../types/system';
import { CurrentUser } from '../../../types/users';
import { AppStateContext } from '../app-state/AppStateContext';
import { CurrentUserContext } from '../current-user/CurrentUserContext';
-import UpdateNotification from '../update-notification/UpdateNotification';
+import { UpdateNotification } from '../update-notification/UpdateNotification';
jest.mock('../../../api/system', () => ({
getSystemUpgrades: jest.fn(),
}));
const ui = {
- updateMessage: byRole('alert'),
- openDialogBtn: byRole('button', { name: 'learn_more' }),
- learnMoreLink: byRole('link', { name: /learn_more/ }),
+ closeDialogBtn: byRole('button', { name: 'cancel' }),
dialog: byRole('dialog', { name: 'system.system_upgrade' }),
+ dialogErrorMessage: byRole('dialog').byText('admin_notification.update.current_version_inactive'),
+ dialogWarningMessage: byRole('dialog').byText('admin_notification.update.new_patch'),
+ hideIntermediateBtn: byRole('button', { name: 'system.hide_intermediate_versions' }),
+ intermediateRegion: byRole('region', { name: 'system.hide_intermediate_versions' }),
latestHeader: byRole('heading', { name: /system.latest_version/ }),
latestLTAHeader: byRole('heading', { name: /system.lta_version/ }),
+ learnMoreLink: byRole('link', { name: /learn_more/ }),
+ openDialogBtn: byRole('button', { name: 'learn_more' }),
patchHeader: byRole('heading', { name: /system.latest_patch/ }),
showIntermediateBtn: byRole('button', { name: 'system.show_intermediate_versions' }),
- hideIntermediateBtn: byRole('button', { name: 'system.hide_intermediate_versions' }),
- intermediateRegion: byRole('region', { name: 'system.hide_intermediate_versions' }),
- dialogWarningMessage: byRole('dialog').byText('admin_notification.update.new_patch'),
- dialogErrorMessage: byRole('dialog').byText('admin_notification.update.current_version_inactive'),
+ updateMessage: byRole('alert'),
};
-it('should not render update notification if user is not logged in', () => {
- renderUpdateNotification(undefined, { isLoggedIn: false });
- expect(getSystemUpgrades).not.toHaveBeenCalled();
- expect(ui.updateMessage.query()).not.toBeInTheDocument();
-});
+const SQCBUpgrade = { downloadUrl: '', product: ProductNameForUpgrade.SonarQubeCommunityBuild };
+const SQSUpgrade = { downloadUrl: '', product: ProductNameForUpgrade.SonarQubeServer };
-it('should not render update notification if user is not admin', () => {
- renderUpdateNotification(undefined, { permissions: { global: [] } });
- expect(getSystemUpgrades).not.toHaveBeenCalled();
- expect(ui.updateMessage.query()).not.toBeInTheDocument();
-});
+describe('when running SQS', () => {
+ it('should not render update notification if user is not logged in', () => {
+ renderUpdateNotification(undefined, { isLoggedIn: false });
+ expect(getSystemUpgrades).not.toHaveBeenCalled();
+ expect(ui.updateMessage.query()).not.toBeInTheDocument();
+ });
-it('should not render update notification if no upgrades', () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('should not render update notification if user is not admin', () => {
+ renderUpdateNotification(undefined, { permissions: { global: [] } });
+ expect(getSystemUpgrades).not.toHaveBeenCalled();
+ expect(ui.updateMessage.query()).not.toBeInTheDocument();
});
- renderUpdateNotification();
- expect(getSystemUpgrades).toHaveBeenCalled();
- expect(ui.updateMessage.query()).not.toBeInTheDocument();
-});
-it('should show error message if upgrades call failed and the version has reached eol', async () => {
- jest.mocked(getSystemUpgrades).mockReturnValue(Promise.reject(new Error('error')));
- renderUpdateNotification(undefined, undefined, {
- versionEOL: formatISO(subDays(new Date(), 1), { representation: 'date' }),
+ it('should not render update notification if no upgrades', () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ renderUpdateNotification();
+ expect(getSystemUpgrades).toHaveBeenCalled();
+ expect(ui.updateMessage.query()).not.toBeInTheDocument();
});
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
- );
- expect(ui.openDialogBtn.query()).not.toBeInTheDocument();
- expect(ui.learnMoreLink.get()).toBeInTheDocument();
-});
-it('should not show the notification banner if there is no network connection and version has not reached the eol', () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [],
+ it('should show error message if upgrades call failed and the version has reached eol', async () => {
+ jest.mocked(getSystemUpgrades).mockReturnValue(Promise.reject(new Error('error')));
+ renderUpdateNotification(undefined, undefined, {
+ versionEOL: formatISO(subDays(new Date(), 1), { representation: 'date' }),
+ });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
+ );
+ expect(ui.openDialogBtn.query()).not.toBeInTheDocument();
+ expect(ui.learnMoreLink.get()).toBeInTheDocument();
});
- renderUpdateNotification(undefined, undefined, {
- versionEOL: formatISO(addDays(new Date(), 1), { representation: 'date' }),
+
+ it('should not show the notification banner if there is no network connection and version has not reached the eol', () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [],
+ });
+ renderUpdateNotification(undefined, undefined, {
+ versionEOL: formatISO(addDays(new Date(), 1), { representation: 'date' }),
+ });
+ expect(ui.updateMessage.query()).not.toBeInTheDocument();
});
- expect(ui.updateMessage.query()).not.toBeInTheDocument();
-});
-it('should show the error banner if there is no network connection and version has reached the eol', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [],
+ it('should show the error banner if there is no network connection and version has reached the eol', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [],
+ });
+ renderUpdateNotification(undefined, undefined, {
+ versionEOL: formatISO(subDays(new Date(), 1), { representation: 'date' }),
+ });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
+ );
+ expect(ui.openDialogBtn.query()).not.toBeInTheDocument();
+ expect(ui.learnMoreLink.get()).toBeInTheDocument();
});
- renderUpdateNotification(undefined, undefined, {
- versionEOL: formatISO(subDays(new Date(), 1), { representation: 'date' }),
+
+ it('active / latest / patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQSUpgrade, version: '10.5.1' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification();
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewPatch}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('10.5.1');
+ expect(ui.patchHeader.get()).toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
- );
- expect(ui.openDialogBtn.query()).not.toBeInTheDocument();
- expect(ui.learnMoreLink.get()).toBeInTheDocument();
-});
-it('active / latest / patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [{ downloadUrl: '', version: '10.5.1' }],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / latest / several patches', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '10.5.2', releaseDate: '2021-08-02' },
+ { ...SQSUpgrade, version: '10.5.1', releaseDate: '2021-08-01' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification();
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewPatch}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('10.5.2');
+ expect(ui.dialog.get()).not.toHaveTextContent('10.5.1');
+ expect(ui.patchHeader.get()).toBeInTheDocument();
+ expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
+ await user.click(ui.showIntermediateBtn.get());
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
+ expect(ui.hideIntermediateBtn.get()).toBeInTheDocument();
+ expect(ui.intermediateRegion.get()).toHaveTextContent('10.5.1August 1, 2021');
});
- const user = userEvent.setup();
- renderUpdateNotification();
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewPatch}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('10.5.1');
- expect(ui.patchHeader.get()).toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('active / latest / several patches', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '10.5.2', releaseDate: '2021-08-02' },
- { downloadUrl: '', version: '10.5.1', releaseDate: '2021-08-01' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / latest / new minor', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQSUpgrade, version: '10.6.0' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification();
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewVersion}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification();
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewPatch}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('10.5.2');
- expect(ui.dialog.get()).not.toHaveTextContent('10.5.1');
- expect(ui.patchHeader.get()).toBeInTheDocument();
- expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
- await user.click(ui.showIntermediateBtn.get());
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
- expect(ui.hideIntermediateBtn.get()).toBeInTheDocument();
- expect(ui.intermediateRegion.get()).toHaveTextContent('10.5.1August 1, 2021');
-});
-it('active / latest / new minor', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [{ downloadUrl: '', version: '10.6.0' }],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / latest / new minor + patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '10.5.1', releaseDate: '2021-08-01' },
+ { ...SQSUpgrade, version: '10.6.0', releaseDate: '2021-08-02' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification();
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewVersion}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.query()).not.toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('10.6.0');
+ expect(ui.dialog.get()).not.toHaveTextContent('10.5.1');
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.patchHeader.query()).not.toBeInTheDocument();
+ expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
+ await user.click(ui.showIntermediateBtn.get());
+ expect(ui.intermediateRegion.get()).toHaveTextContent('10.5.1August 1, 2021');
});
- const user = userEvent.setup();
- renderUpdateNotification();
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewVersion}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('active / latest / new minor + patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '10.5.1', releaseDate: '2021-08-01' },
- { downloadUrl: '', version: '10.6.0', releaseDate: '2021-08-02' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('no longer active / latest / new minor', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '10.6.0', releaseDate: '2021-08-02' },
+ { ...SQSUpgrade, version: '10.7.0', releaseDate: '2021-08-03' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: false,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification();
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('10.7.0');
+ expect(ui.dialog.get()).not.toHaveTextContent('10.6.0');
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
+ await user.click(ui.showIntermediateBtn.get());
+ expect(ui.intermediateRegion.get()).toHaveTextContent('10.6.0August 2, 2021');
});
- const user = userEvent.setup();
- renderUpdateNotification();
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewVersion}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.query()).not.toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('10.6.0');
- expect(ui.dialog.get()).not.toHaveTextContent('10.5.1');
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.patchHeader.query()).not.toBeInTheDocument();
- expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
- await user.click(ui.showIntermediateBtn.get());
- expect(ui.intermediateRegion.get()).toHaveTextContent('10.5.1August 1, 2021');
-});
-it('no longer active / latest / new minor', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '10.6.0', releaseDate: '2021-08-02' },
- { downloadUrl: '', version: '10.7.0', releaseDate: '2021-08-03' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: false,
+ it('no longer active / latest / new minor + patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '10.5.1', releaseDate: '2021-08-01' },
+ { ...SQSUpgrade, version: '10.6.0', releaseDate: '2021-08-02' },
+ { ...SQSUpgrade, version: '10.7.0', releaseDate: '2021-08-03' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: false,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification();
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
+ expect(ui.dialogWarningMessage.query()).not.toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('10.7.0');
+ expect(ui.dialog.get()).not.toHaveTextContent('10.6.0');
+ expect(ui.dialog.get()).not.toHaveTextContent('10.5.1');
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.patchHeader.query()).not.toBeInTheDocument();
+ expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
+ await user.click(ui.showIntermediateBtn.get());
+ expect(ui.intermediateRegion.get()).toHaveTextContent('10.6.0August 2, 2021');
+ expect(ui.intermediateRegion.get()).toHaveTextContent('10.5.1August 1, 2021');
});
- const user = userEvent.setup();
- renderUpdateNotification();
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('10.7.0');
- expect(ui.dialog.get()).not.toHaveTextContent('10.6.0');
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
- await user.click(ui.showIntermediateBtn.get());
- expect(ui.intermediateRegion.get()).toHaveTextContent('10.6.0August 2, 2021');
-});
-it('no longer active / latest / new minor + patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '10.5.1', releaseDate: '2021-08-01' },
- { downloadUrl: '', version: '10.6.0', releaseDate: '2021-08-02' },
- { downloadUrl: '', version: '10.7.0', releaseDate: '2021-08-03' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: false,
+ it('active / lta / patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQSUpgrade, version: '9.9.1' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(undefined, undefined, { version: '9.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewPatch}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('9.9.1');
+ expect(ui.latestLTAHeader.get()).toBeInTheDocument();
+ // If the current version is an LTA version, we don't show Patch header, we show Latest LTA header
+ expect(ui.patchHeader.query()).not.toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification();
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
- expect(ui.dialogWarningMessage.query()).not.toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('10.7.0');
- expect(ui.dialog.get()).not.toHaveTextContent('10.6.0');
- expect(ui.dialog.get()).not.toHaveTextContent('10.5.1');
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.patchHeader.query()).not.toBeInTheDocument();
- expect(ui.showIntermediateBtn.get()).toBeInTheDocument();
- await user.click(ui.showIntermediateBtn.get());
- expect(ui.intermediateRegion.get()).toHaveTextContent('10.6.0August 2, 2021');
- expect(ui.intermediateRegion.get()).toHaveTextContent('10.5.1August 1, 2021');
-});
-it('active / lta / patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [{ downloadUrl: '', version: '9.9.1' }],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / lta / new minor', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQSUpgrade, version: '10.0.0' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(undefined, undefined, { version: '9.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewVersion}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.query()).not.toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('10.0.0');
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.latestLTAHeader.query()).not.toBeInTheDocument();
+ expect(ui.patchHeader.query()).not.toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '9.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewPatch}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('9.9.1');
- expect(ui.latestLTAHeader.get()).toBeInTheDocument();
- // If the current version is an LTA version, we don't show Patch header, we show Latest LTA header
- expect(ui.patchHeader.query()).not.toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('active / lta / new minor', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [{ downloadUrl: '', version: '10.0.0' }],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / lta / new minor + patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '9.9.1' },
+ { ...SQSUpgrade, version: '10.0.0' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(undefined, undefined, { version: '9.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewPatch}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('10.0.0');
+ expect(ui.dialog.get()).toHaveTextContent('9.9.1');
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.latestLTAHeader.get()).toBeInTheDocument();
+ // If the current version is an LTA version, we don't show Patch header, we show Latest LTA header
+ expect(ui.patchHeader.query()).not.toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '9.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewVersion}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.query()).not.toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('10.0.0');
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.latestLTAHeader.query()).not.toBeInTheDocument();
- expect(ui.patchHeader.query()).not.toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('active / lta / new minor + patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '9.9.1' },
- { downloadUrl: '', version: '10.0.0' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / prev lta / new lta + patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '8.9.1' },
+ { ...SQSUpgrade, version: '9.9.0' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewPatch}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('8.9.1');
+ expect(ui.dialog.get()).toHaveTextContent('9.9.0');
+ expect(ui.latestHeader.query()).not.toBeInTheDocument();
+ expect(ui.latestLTAHeader.get()).toBeInTheDocument();
+ expect(ui.patchHeader.get()).toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '9.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewPatch}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('10.0.0');
- expect(ui.dialog.get()).toHaveTextContent('9.9.1');
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.latestLTAHeader.get()).toBeInTheDocument();
- // If the current version is an LTA version, we don't show Patch header, we show Latest LTA header
- expect(ui.patchHeader.query()).not.toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('active / prev lta / new lta + patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '8.9.1' },
- { downloadUrl: '', version: '9.9.0' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / prev lta / new lta + new minor + patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '8.9.1' },
+ { ...SQSUpgrade, version: '9.9.0' },
+ { ...SQSUpgrade, version: '10.0.0' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.NewPatch}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('8.9.1');
+ expect(ui.dialog.get()).toHaveTextContent('9.9.0');
+ expect(ui.dialog.get()).toHaveTextContent('10.0.0');
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.latestLTAHeader.get()).toBeInTheDocument();
+ expect(ui.patchHeader.get()).toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewPatch}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('8.9.1');
- expect(ui.dialog.get()).toHaveTextContent('9.9.0');
- expect(ui.latestHeader.query()).not.toBeInTheDocument();
- expect(ui.latestLTAHeader.get()).toBeInTheDocument();
- expect(ui.patchHeader.get()).toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('active / prev lta / new lta + new minor + patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '8.9.1' },
- { downloadUrl: '', version: '9.9.0' },
- { downloadUrl: '', version: '10.0.0' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: true,
+ it('active / no uogrades', () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+
+ renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
+
+ expect(screen.queryByText('admin_notification.update')).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.NewPatch}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogWarningMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('8.9.1');
- expect(ui.dialog.get()).toHaveTextContent('9.9.0');
- expect(ui.dialog.get()).toHaveTextContent('10.0.0');
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.latestLTAHeader.get()).toBeInTheDocument();
- expect(ui.patchHeader.get()).toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('no longer active / prev lta / new lta', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [{ downloadUrl: '', version: '9.9.0' }],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: false,
+ it('no longer active / prev lta / new lta', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQSUpgrade, version: '9.9.0' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: false,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('9.9.0');
+ expect(ui.latestLTAHeader.get()).toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('9.9.0');
- expect(ui.latestLTAHeader.get()).toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
-});
-it('no longer active / prev lta / new lta + patch', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '8.9.1' },
- { downloadUrl: '', version: '9.9.0' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: false,
+ it('no longer active / prev lta / new lta + patch', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '8.9.1' },
+ { ...SQSUpgrade, version: '9.9.0' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: false,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('9.9.0');
+ expect(ui.dialog.get()).not.toHaveTextContent('8.9.1');
+ expect(ui.latestLTAHeader.get()).toBeInTheDocument();
+ expect(ui.patchHeader.query()).not.toBeInTheDocument();
+ expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
+ });
+
+ it('no longer active / prev lta / new lta + patch + new minors', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQSUpgrade, version: '8.9.1', releaseDate: '2021-08-01' },
+ { ...SQSUpgrade, version: '9.9.0', releaseDate: '2021-08-02' },
+ { ...SQSUpgrade, version: '9.9.1', releaseDate: '2021-08-03' },
+ { ...SQSUpgrade, version: '10.0.0', releaseDate: '2021-08-04' },
+ { ...SQSUpgrade, version: '10.1.0', releaseDate: '2021-08-05' },
+ { ...SQSUpgrade, version: '10.1.1', releaseDate: '2021-08-06' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: false,
+ });
+ const user = userEvent.setup();
+ renderUpdateNotification(true, undefined, { version: '8.9.0' });
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
+ );
+ await user.click(ui.openDialogBtn.get());
+ expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
+ expect(ui.dialog.get()).toHaveTextContent('9.9.1');
+ expect(ui.dialog.get()).not.toHaveTextContent('9.9.0');
+ expect(ui.dialog.get()).toHaveTextContent('10.1.1');
+ expect(ui.dialog.get()).not.toHaveTextContent('10.1.0');
+ expect(ui.dialog.get()).not.toHaveTextContent('10.0.0');
+ expect(ui.dialog.get()).not.toHaveTextContent('8.9.1');
+ expect(ui.latestHeader.get()).toBeInTheDocument();
+ expect(ui.latestLTAHeader.get()).toBeInTheDocument();
+ expect(ui.patchHeader.query()).not.toBeInTheDocument();
+ expect(ui.showIntermediateBtn.getAll()).toHaveLength(2);
+ await user.click(ui.showIntermediateBtn.getAt(0));
+ expect(ui.intermediateRegion.get()).toHaveTextContent('10.1.0August 5, 2021');
+ expect(ui.intermediateRegion.get()).toHaveTextContent('10.0.0August 4, 2021');
+ expect(ui.intermediateRegion.get()).not.toHaveTextContent('9.9.0');
+ await user.click(ui.hideIntermediateBtn.get());
+ await user.click(ui.showIntermediateBtn.getAt(1));
+ expect(ui.intermediateRegion.get()).toHaveTextContent('9.9.0August 2, 2021');
+ expect(ui.intermediateRegion.get()).not.toHaveTextContent('10.1.0');
+ expect(ui.intermediateRegion.get()).not.toHaveTextContent('10.0.0');
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('9.9.0');
- expect(ui.dialog.get()).not.toHaveTextContent('8.9.1');
- expect(ui.latestLTAHeader.get()).toBeInTheDocument();
- expect(ui.patchHeader.query()).not.toBeInTheDocument();
- expect(ui.showIntermediateBtn.query()).not.toBeInTheDocument();
});
-it('no longer active / prev lta / new lta + patch + new minors', async () => {
- jest.mocked(getSystemUpgrades).mockResolvedValue({
- upgrades: [
- { downloadUrl: '', version: '8.9.1', releaseDate: '2021-08-01' },
- { downloadUrl: '', version: '9.9.0', releaseDate: '2021-08-02' },
- { downloadUrl: '', version: '9.9.1', releaseDate: '2021-08-03' },
- { downloadUrl: '', version: '10.0.0', releaseDate: '2021-08-04' },
- { downloadUrl: '', version: '10.1.0', releaseDate: '2021-08-05' },
- { downloadUrl: '', version: '10.1.1', releaseDate: '2021-08-06' },
- ],
- latestLTA: '9.9',
- updateCenterRefresh: '',
- installedVersionActive: false,
+describe('when running SQCB', () => {
+ it('should not render update notification if no upgrades', () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+
+ renderUpdateNotification(undefined, undefined, { edition: EditionKey.community });
+
+ expect(getSystemUpgrades).toHaveBeenCalled();
+
+ expect(ui.updateMessage.query()).not.toBeInTheDocument();
+ });
+
+ it('SQCB upgrade available, no SQS upgrade', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQCBUpgrade, version: '25.2' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+
+ renderUpdateNotification(undefined, undefined, { edition: EditionKey.community });
+
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ 'admin_notification.update.new_sqcb_version',
+ );
+
+ expect(
+ screen.queryByText('admin_notification.update.new_sqs_version_when_running_sqcb.banner'),
+ ).not.toBeInTheDocument();
+ });
+
+ it('non-patch SQS upgrade available, no SQCB upgrade', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQSUpgrade, version: '2025.2' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+
+ const user = userEvent.setup();
+
+ renderUpdateNotification(undefined, undefined, { edition: EditionKey.community });
+
+ expect(
+ screen.queryByText('admin_notification.update.new_sqcb_version'),
+ ).not.toBeInTheDocument();
+
+ expect(await ui.updateMessage.find()).toHaveTextContent(
+ 'admin_notification.update.new_sqs_version_when_running_sqcb.banner',
+ );
+
+ await user.click(ui.openDialogBtn.get());
+
+ expect(
+ await screen.findByText(
+ 'admin_notification.update.new_sqs_version_when_running_sqcb.upgrade',
+ ),
+ ).toBeInTheDocument();
+
+ await user.click(ui.closeDialogBtn.get());
+
+ expect(
+ screen.queryByText('admin_notification.update.new_sqs_version_when_running_sqcb.upgrade'),
+ ).not.toBeInTheDocument();
+ });
+
+ it('only patch SQS upgrade available, no SQCB upgrade', () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [{ ...SQSUpgrade, version: '2025.2.3' }],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+
+ renderUpdateNotification(undefined, undefined, { edition: EditionKey.community });
+
+ expect(
+ screen.queryByText('admin_notification.update.new_sqcb_version'),
+ ).not.toBeInTheDocument();
+
+ expect(
+ screen.queryByText('admin_notification.update.new_sqs_version_when_running_sqcb.banner'),
+ ).not.toBeInTheDocument();
+ });
+
+ it('both SQCB and non-patch-SQS upgrades available', async () => {
+ jest.mocked(getSystemUpgrades).mockResolvedValue({
+ upgrades: [
+ { ...SQCBUpgrade, version: '25.4' },
+ { ...SQSUpgrade, version: '2025.4' },
+ ],
+ latestLTA: '9.9',
+ updateCenterRefresh: '',
+ installedVersionActive: true,
+ });
+
+ const user = userEvent.setup();
+
+ renderUpdateNotification(true, undefined, { edition: EditionKey.community });
+
+ expect(await ui.updateMessage.findAll()).toHaveLength(2);
+
+ await user.click(ui.openDialogBtn.get());
+
+ expect(
+ await screen.findByText(
+ 'admin_notification.update.new_sqs_version_when_running_sqcb.upgrade',
+ ),
+ ).toBeInTheDocument();
+
+ await user.keyboard('{Escape}');
+
+ expect(
+ screen.queryByText('admin_notification.update.new_sqs_version_when_running_sqcb.upgrade'),
+ ).not.toBeInTheDocument();
});
- const user = userEvent.setup();
- renderUpdateNotification(undefined, undefined, { version: '8.9.0' });
- expect(await ui.updateMessage.find()).toHaveTextContent(
- `admin_notification.update.${UpdateUseCase.CurrentVersionInactive}`,
- );
- await user.click(ui.openDialogBtn.get());
- expect(ui.dialogErrorMessage.get()).toBeInTheDocument();
- expect(ui.dialog.get()).toHaveTextContent('9.9.1');
- expect(ui.dialog.get()).not.toHaveTextContent('9.9.0');
- expect(ui.dialog.get()).toHaveTextContent('10.1.1');
- expect(ui.dialog.get()).not.toHaveTextContent('10.1.0');
- expect(ui.dialog.get()).not.toHaveTextContent('10.0.0');
- expect(ui.dialog.get()).not.toHaveTextContent('8.9.1');
- expect(ui.latestHeader.get()).toBeInTheDocument();
- expect(ui.latestLTAHeader.get()).toBeInTheDocument();
- expect(ui.patchHeader.query()).not.toBeInTheDocument();
- expect(ui.showIntermediateBtn.getAll()).toHaveLength(2);
- await user.click(ui.showIntermediateBtn.getAt(0));
- expect(ui.intermediateRegion.get()).toHaveTextContent('10.1.0August 5, 2021');
- expect(ui.intermediateRegion.get()).toHaveTextContent('10.0.0August 4, 2021');
- expect(ui.intermediateRegion.get()).not.toHaveTextContent('9.9.0');
- await user.click(ui.hideIntermediateBtn.get());
- await user.click(ui.showIntermediateBtn.getAt(1));
- expect(ui.intermediateRegion.get()).toHaveTextContent('9.9.0August 2, 2021');
- expect(ui.intermediateRegion.get()).not.toHaveTextContent('10.1.0');
- expect(ui.intermediateRegion.get()).not.toHaveTextContent('10.0.0');
});
function renderUpdateNotification(
- dissmissable: boolean = false,
+ dissmissable = false,
user?: Partial<CurrentUser>,
- // versionEOL is a date in the past to be sure that it is not used when we have data from upgrades endpoint
- appState: Partial<AppState> = { version: '10.5.0', versionEOL: '2020-01-01' },
+ appState: Partial<AppState> = {},
) {
return renderComponent(
<CurrentUserContext.Provider
@@ -478,7 +621,15 @@ function renderUpdateNotification(
updateDismissedNotices: () => {},
}}
>
- <AppStateContext.Provider value={mockAppState(appState)}>
+ <AppStateContext.Provider
+ value={mockAppState({
+ edition: EditionKey.developer,
+ version: '10.5.0',
+ // versionEOL is a date in the past to be sure that it is not used when we have data from upgrades endpoint
+ versionEOL: '2020-01-01',
+ ...appState,
+ })}
+ >
<UpdateNotification dismissable={dissmissable} />
</AppStateContext.Provider>
</CurrentUserContext.Provider>,
diff --git a/server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx b/server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx
index 1499bc7f3c9..3252f4890e0 100644
--- a/server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx
+++ b/server/sonar-web/src/main/js/app/components/calculation-notification/CalculationChangeMessage.tsx
@@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
import { useLocation } from '~sonar-aligned/components/hoc/withRouter';
import { ComponentQualifier } from '~sonar-aligned/types/component';
import DocumentationLink from '../../../components/common/DocumentationLink';
-import DismissableAlert from '../../../components/ui/DismissableAlert';
+import { DismissableAlert } from '../../../components/ui/DismissableAlert';
import { DocLink } from '../../../helpers/doc-links';
import { translate } from '../../../helpers/l10n';
import { useStandardExperienceModeQuery } from '../../../queries/mode';
@@ -49,7 +49,7 @@ export default function CalculationChangeMessage() {
id={`notification.calculation_change.message.${SHOW_MESSAGE_PATHS[location.pathname]}`}
values={{
link: (
- <DocumentationLink to={DocLink.MetricDefinitions}>
+ <DocumentationLink className="sw-ml-1" to={DocLink.MetricDefinitions}>
{translate('learn_more')}
</DocumentationLink>
),
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/SQCBUpdateBanners.tsx b/server/sonar-web/src/main/js/app/components/update-notification/SQCBUpdateBanners.tsx
new file mode 100644
index 00000000000..db9b26758ee
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/update-notification/SQCBUpdateBanners.tsx
@@ -0,0 +1,128 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 { LinkStandalone } from '@sonarsource/echoes-react';
+import { isEmpty } from 'lodash';
+import { FormattedMessage } from 'react-intl';
+import { Banner } from '~design-system';
+import { getSystemUpgrades } from '../../../api/system';
+import { DismissableAlert } from '../../../components/ui/DismissableAlert';
+import { SystemUpgradeButton } from '../../../components/upgrade/SystemUpgradeButton';
+import { UpdateUseCase } from '../../../components/upgrade/utils';
+import { translate } from '../../../helpers/l10n';
+import { ProductNameForUpgrade } from '../../../types/system';
+import { useAppState } from '../app-state/withAppStateContext';
+import { analyzeUpgrades, isVersionAPatchUpdate, parseVersion } from './helpers';
+
+interface Props {
+ data?: Awaited<ReturnType<typeof getSystemUpgrades>>;
+ dismissable?: boolean;
+}
+
+export function SQCBUpdateBanners({ data, dismissable }: Readonly<Props>) {
+ const appState = useAppState();
+
+ const parsedVersion = parseVersion(appState.version);
+ const { upgrades = [], latestLTA } = data ?? {};
+
+ const SQSUpgrades = upgrades.filter(
+ (upgrade) =>
+ upgrade.product === ProductNameForUpgrade.SonarQubeServer &&
+ !isVersionAPatchUpdate(upgrade.version),
+ );
+
+ const SQCBUpgrades = upgrades.filter(
+ (upgrade) => upgrade.product === ProductNameForUpgrade.SonarQubeCommunityBuild,
+ );
+
+ const banners = [];
+
+ if (!isEmpty(SQCBUpgrades)) {
+ const contents = (
+ <FormattedMessage
+ id="admin_notification.update.new_sqcb_version"
+ values={{
+ link: (
+ <LinkStandalone
+ className="sw-ml-1"
+ to="https://www.sonarsource.com/open-source-editions/sonarqube-community-edition/"
+ >
+ {translate('admin_notification.update.latest')}
+ </LinkStandalone>
+ ),
+ }}
+ />
+ );
+
+ const { latest } = analyzeUpgrades({
+ parsedVersion,
+ upgrades: SQCBUpgrades,
+ });
+
+ const dismissKey = latest?.version ?? appState.version;
+
+ banners.push(
+ dismissable ? (
+ <DismissableAlert alertKey={dismissKey} key="SQCB" variant="info">
+ {contents}
+ </DismissableAlert>
+ ) : (
+ <Banner key="SQCB" variant="info">
+ {contents}
+ </Banner>
+ ),
+ );
+ }
+
+ if (!isEmpty(SQSUpgrades)) {
+ const { latest } = analyzeUpgrades({
+ parsedVersion,
+ upgrades: SQSUpgrades,
+ });
+
+ const contents = (
+ <>
+ {translate('admin_notification.update.new_sqs_version_when_running_sqcb.banner')}{' '}
+ {translate('admin_notification.update.new_sqs_version_when_running_sqcb.upgrade')}.
+ <SystemUpgradeButton
+ systemUpgrades={[latest]}
+ updateUseCase={UpdateUseCase.NewVersion}
+ latestLTA={latestLTA}
+ />
+ </>
+ );
+
+ const dismissKey = latest?.version ?? appState.version;
+
+ banners.push(
+ dismissable ? (
+ <DismissableAlert alertKey={dismissKey} key="SQS" variant="info">
+ {contents}
+ </DismissableAlert>
+ ) : (
+ <Banner key="SQS" variant="info">
+ {contents}
+ </Banner>
+ ),
+ );
+ }
+
+ return banners;
+}
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/SQSUpdateBanner.tsx b/server/sonar-web/src/main/js/app/components/update-notification/SQSUpdateBanner.tsx
new file mode 100644
index 00000000000..f85348642c3
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/update-notification/SQSUpdateBanner.tsx
@@ -0,0 +1,99 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 { isEmpty } from 'lodash';
+import { Banner } from '~design-system';
+import { getSystemUpgrades } from '../../../api/system';
+import { DismissableAlert } from '../../../components/ui/DismissableAlert';
+import { SystemUpgradeButton } from '../../../components/upgrade/SystemUpgradeButton';
+import { UpdateUseCase } from '../../../components/upgrade/utils';
+import { translate } from '../../../helpers/l10n';
+import { isCurrentVersionEOLActive } from '../../../helpers/system';
+import { ProductNameForUpgrade } from '../../../types/system';
+import { useAppState } from '../app-state/withAppStateContext';
+import { analyzeUpgrades, BANNER_VARIANT, isCurrentVersionLTA, parseVersion } from './helpers';
+
+interface Props {
+ data?: Awaited<ReturnType<typeof getSystemUpgrades>>;
+ dismissable?: boolean;
+}
+
+export function SQSUpdateBanner({ data, dismissable }: Readonly<Props>) {
+ const appState = useAppState();
+
+ // below: undefined already tested upstream in UpdateNotification, ?? [] is just to make TS happy
+ const parsedVersion = parseVersion(appState.version) ?? [];
+ const { upgrades = [], installedVersionActive, latestLTA } = data ?? {};
+
+ const SQSUpgrades = upgrades.filter(
+ (upgrade) => upgrade.product === ProductNameForUpgrade.SonarQubeServer,
+ );
+
+ const active = installedVersionActive ?? isCurrentVersionEOLActive(appState.versionEOL);
+
+ if (active && isEmpty(SQSUpgrades)) {
+ return null;
+ }
+
+ const { isMinorUpdate, isPatchUpdate, latest } = analyzeUpgrades({
+ parsedVersion,
+ upgrades: SQSUpgrades,
+ });
+
+ let useCase = UpdateUseCase.NewVersion;
+
+ if (!active) {
+ useCase = UpdateUseCase.CurrentVersionInactive;
+ } else if (
+ isPatchUpdate &&
+ // if the latest update is a patch and either we're running latest LTA, or there's no minor update
+ ((latestLTA !== undefined && isCurrentVersionLTA(parsedVersion, latestLTA)) || !isMinorUpdate)
+ ) {
+ useCase = UpdateUseCase.NewPatch;
+ }
+
+ const dismissKey = useCase + (latest?.version ?? appState.version);
+
+ const contents = (
+ <>
+ {translate('admin_notification.update', useCase)}
+
+ <SystemUpgradeButton
+ systemUpgrades={SQSUpgrades}
+ updateUseCase={useCase}
+ latestLTA={latestLTA}
+ />
+ </>
+ );
+
+ return dismissable ? (
+ <DismissableAlert
+ alertKey={dismissKey}
+ variant={BANNER_VARIANT[useCase]}
+ className={`it__promote-update-notification it__upgrade-prompt-${useCase}`}
+ >
+ {contents}
+ </DismissableAlert>
+ ) : (
+ <Banner variant={BANNER_VARIANT[useCase]} className={`it__upgrade-prompt-${useCase}`}>
+ {contents}
+ </Banner>
+ );
+}
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
index 40fdb8b963a..0d13adce1be 100644
--- a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
+++ b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
@@ -18,113 +18,48 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { groupBy, isEmpty, mapValues } from 'lodash';
-import { Banner } from '~design-system';
-import DismissableAlert from '../../../components/ui/DismissableAlert';
-import SystemUpgradeButton from '../../../components/upgrade/SystemUpgradeButton';
-import { UpdateUseCase } from '../../../components/upgrade/utils';
-import { translate } from '../../../helpers/l10n';
-import { isCurrentVersionEOLActive } from '../../../helpers/system';
+import { isEmpty } from 'lodash';
import { hasGlobalPermission } from '../../../helpers/users';
import { useSystemUpgrades } from '../../../queries/system';
+import { EditionKey } from '../../../types/editions';
import { Permissions } from '../../../types/permissions';
import { isLoggedIn } from '../../../types/users';
import { useAppState } from '../app-state/withAppStateContext';
import { useCurrentUser } from '../current-user/CurrentUserContext';
-import { BANNER_VARIANT, isCurrentVersionLTA, isMinorUpdate, isPatchUpdate } from './helpers';
+import { parseVersion } from './helpers';
+import { SQCBUpdateBanners } from './SQCBUpdateBanners';
+import { SQSUpdateBanner } from './SQSUpdateBanner';
interface Props {
dismissable?: boolean;
}
-const VERSION_PARSER = /^(\d+)\.(\d+)(\.(\d+))?/;
-
-export default function UpdateNotification({ dismissable }: Readonly<Props>) {
+export function UpdateNotification({ dismissable }: Readonly<Props>) {
const appState = useAppState();
const { currentUser } = useCurrentUser();
const canUserSeeNotification =
isLoggedIn(currentUser) && hasGlobalPermission(currentUser, Permissions.Admin);
- const regExpParsedVersion = VERSION_PARSER.exec(appState.version);
+
+ const parsedVersion = parseVersion(appState.version);
const { data, isLoading } = useSystemUpgrades({
- enabled: canUserSeeNotification && regExpParsedVersion !== null,
+ enabled: canUserSeeNotification && parsedVersion !== undefined,
});
- if (!canUserSeeNotification || regExpParsedVersion === null || isLoading) {
+ if (!canUserSeeNotification || parsedVersion === undefined || isLoading) {
return null;
}
- const { upgrades = [], installedVersionActive, latestLTA } = data ?? {};
-
- let active = installedVersionActive;
+ const isCommunityBuildRunning = appState.edition === EditionKey.community;
- if (installedVersionActive === undefined) {
- active = isCurrentVersionEOLActive(appState.versionEOL);
- }
+ if (isCommunityBuildRunning && !isEmpty(data?.upgrades)) {
+ // We're running SQCB, show SQCB update banner & SQS update banner if applicable
- if (active && isEmpty(upgrades)) {
- return null;
+ return <SQCBUpdateBanners data={data} dismissable={dismissable} />;
}
- const parsedVersion = regExpParsedVersion
- .slice(1)
- .map(Number)
- .map((n) => (isNaN(n) ? 0 : n));
-
- const systemUpgrades = mapValues(
- groupBy(upgrades, (upgrade) => {
- const [major] = upgrade.version.split('.');
- return major;
- }),
- (upgrades) =>
- groupBy(upgrades, (upgrade) => {
- const [, minor] = upgrade.version.split('.');
- return minor;
- }),
- );
-
- let useCase = UpdateUseCase.NewVersion;
-
- if (!active) {
- useCase = UpdateUseCase.CurrentVersionInactive;
- } else if (
- isPatchUpdate(parsedVersion, systemUpgrades) &&
- ((latestLTA !== undefined && isCurrentVersionLTA(parsedVersion, latestLTA)) ||
- !isMinorUpdate(parsedVersion, systemUpgrades))
- ) {
- useCase = UpdateUseCase.NewPatch;
- }
-
- const latest = [...upgrades].sort(
- (upgrade1, upgrade2) =>
- new Date(upgrade2.releaseDate ?? '').getTime() -
- new Date(upgrade1.releaseDate ?? '').getTime(),
- )[0];
-
- const dismissKey = useCase + (latest?.version ?? appState.version);
+ // We're running SQS (or old SQ), only show SQS update banner if applicable
- return dismissable ? (
- <DismissableAlert
- alertKey={dismissKey}
- variant={BANNER_VARIANT[useCase]}
- className={`it__promote-update-notification it__upgrade-prompt-${useCase}`}
- >
- {translate('admin_notification.update', useCase)}
- <SystemUpgradeButton
- systemUpgrades={upgrades}
- updateUseCase={useCase}
- latestLTA={latestLTA}
- />
- </DismissableAlert>
- ) : (
- <Banner variant={BANNER_VARIANT[useCase]} className={`it__upgrade-prompt-${useCase}`}>
- {translate('admin_notification.update', useCase)}
- <SystemUpgradeButton
- systemUpgrades={upgrades}
- updateUseCase={useCase}
- latestLTA={latestLTA}
- />
- </Banner>
- );
+ return <SQSUpdateBanner data={data} dismissable={dismissable} />;
}
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts b/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts
index 0eb3b558b84..5092aa7bda9 100644
--- a/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts
+++ b/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { isEmpty } from 'lodash';
+import { groupBy, isEmpty, mapValues } from 'lodash';
import { Variant } from '~design-system';
import { UpdateUseCase, sortUpgrades } from '../../../components/upgrade/utils';
import { SystemUpgrade } from '../../../types/system';
@@ -28,6 +28,38 @@ type GroupedSystemUpdate = {
[x: string]: Record<string, SystemUpgrade[]>;
};
+export const analyzeUpgrades = ({
+ parsedVersion = [],
+ upgrades,
+}: {
+ parsedVersion: number[] | undefined;
+ upgrades: SystemUpgrade[];
+}) => {
+ const systemUpgrades = mapValues(
+ groupBy(upgrades, (upgrade: SystemUpgrade) => {
+ const [major] = upgrade.version.split('.');
+ return major;
+ }),
+ (upgrades) =>
+ groupBy(upgrades, (upgrade: SystemUpgrade) => {
+ const [, minor] = upgrade.version.split('.');
+ return minor;
+ }),
+ );
+
+ const latest = [...upgrades].sort(
+ (upgrade1, upgrade2) =>
+ new Date(upgrade2.releaseDate ?? '').getTime() -
+ new Date(upgrade1.releaseDate ?? '').getTime(),
+ )[0];
+
+ return {
+ isMinorUpdate: isMinorUpdate(parsedVersion, systemUpgrades),
+ isPatchUpdate: isLatestUpdatedAPatchUpdate(parsedVersion, systemUpgrades),
+ latest,
+ };
+};
+
export const isCurrentVersionLTA = (parsedVersion: number[], latestLTS: string) => {
const [currentMajor, currentMinor] = parsedVersion;
const [ltsMajor, ltsMinor] = latestLTS.split('.').map(Number);
@@ -36,13 +68,17 @@ export const isCurrentVersionLTA = (parsedVersion: number[], latestLTS: string)
export const isMinorUpdate = (parsedVersion: number[], systemUpgrades: GroupedSystemUpdate) => {
const [currentMajor, currentMinor] = parsedVersion;
- const allMinor = systemUpgrades[currentMajor];
+ const allMinor = systemUpgrades[currentMajor] ?? {};
+
return Object.keys(allMinor)
.map(Number)
.some((minor) => minor > currentMinor);
};
-export const isPatchUpdate = (parsedVersion: number[], systemUpgrades: GroupedSystemUpdate) => {
+export const isLatestUpdatedAPatchUpdate = (
+ parsedVersion: number[],
+ systemUpgrades: GroupedSystemUpdate,
+) => {
const [currentMajor, currentMinor, currentPatch] = parsedVersion;
const allMinor = systemUpgrades[currentMajor];
const allPatch = sortUpgrades(allMinor?.[currentMinor] ?? []);
@@ -51,11 +87,26 @@ export const isPatchUpdate = (parsedVersion: number[], systemUpgrades: GroupedSy
const [, , latestPatch] = allPatch[0].version.split('.').map(Number);
const effectiveCurrentPatch = isNaN(currentPatch) ? 0 : currentPatch;
const effectiveLatestPatch = isNaN(latestPatch) ? 0 : latestPatch;
+
return effectiveCurrentPatch < effectiveLatestPatch;
}
+
return false;
};
+export const parseVersion = (version: string) => {
+ const VERSION_PARSER = /^(\d+)\.(\d+)(\.(\d+))?/;
+ const regExpParsedVersion = VERSION_PARSER.exec(version);
+
+ return regExpParsedVersion
+ ?.slice(1)
+ .map(Number)
+ .map((n) => (isNaN(n) ? 0 : n));
+};
+
+export const isVersionAPatchUpdate = (version: string) =>
+ ((parseVersion(version) ?? [])[2] ?? 0) !== 0;
+
export const BANNER_VARIANT: Dict<Variant> = {
[UpdateUseCase.NewVersion]: 'info',
[UpdateUseCase.CurrentVersionInactive]: 'error',
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx b/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx
index 7a095b6652b..9784787bb1e 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/FirstAnalysisNextStepsNotif.tsx
@@ -23,7 +23,7 @@ import { Link } from '~design-system';
import { queryToSearchString } from '~sonar-aligned/helpers/urls';
import { ComponentQualifier } from '~sonar-aligned/types/component';
import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
-import DismissableAlert from '../../../components/ui/DismissableAlert';
+import { DismissableAlert } from '../../../components/ui/DismissableAlert';
import { translate } from '../../../helpers/l10n';
import { useProjectBindingQuery } from '../../../queries/devops-integration';
import { Component } from '../../../types/types';
diff --git a/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx b/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx
index 0d142ef634e..141515a8397 100644
--- a/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx
+++ b/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx
@@ -24,7 +24,7 @@ import { LargeCenteredLayout, PageContentFontWrapper } from '~design-system';
import { withRouter } from '~sonar-aligned/components/hoc/withRouter';
import { Location, Router } from '~sonar-aligned/types/router';
import { getSystemInfo } from '../../../api/system';
-import UpdateNotification from '../../../app/components/update-notification/UpdateNotification';
+import { UpdateNotification } from '../../../app/components/update-notification/UpdateNotification';
import { translate } from '../../../helpers/l10n';
import { SysInfoCluster, SysInfoStandalone } from '../../../types/types';
import '../styles.css';
diff --git a/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx b/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
index 34bed88ab14..98a6bb91a17 100644
--- a/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
+++ b/server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
@@ -32,7 +32,7 @@ export interface DismissableAlertProps {
export const DISMISSED_ALERT_STORAGE_KEY = 'sonarqube.dismissed_alert';
-export default function DismissableAlert(props: DismissableAlertProps) {
+export function DismissableAlert(props: DismissableAlertProps) {
const { alertKey, children, className, variant } = props;
const [show, setShow] = React.useState(false);
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
index d3f89677405..f29ce936bf0 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
@@ -18,12 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Link } from '@sonarsource/echoes-react';
-import * as React from 'react';
-import { ButtonSecondary } from '~design-system';
+import { Button, Link } from '@sonarsource/echoes-react';
+import React from 'react';
import { translate } from '../../helpers/l10n';
import { SystemUpgrade } from '../../types/system';
-import SystemUpgradeForm from './SystemUpgradeForm';
+import { SystemUpgradeForm } from './SystemUpgradeForm';
import { groupUpgrades, sortUpgrades, UpdateUseCase } from './utils';
interface Props {
@@ -32,18 +31,18 @@ interface Props {
updateUseCase: UpdateUseCase;
}
-export default function SystemUpgradeButton(props: Readonly<Props>) {
+export function SystemUpgradeButton(props: Readonly<Props>) {
const { latestLTA, systemUpgrades, updateUseCase } = props;
- const [isSystemUpgradeFormOpen, setSystemUpgradeFormOpen] = React.useState(false);
+ const [isSystemUpgradeFormOpen, setIsSystemUpgradeFormOpen] = React.useState(false);
const openSystemUpgradeForm = React.useCallback(() => {
- setSystemUpgradeFormOpen(true);
- }, [setSystemUpgradeFormOpen]);
+ setIsSystemUpgradeFormOpen(true);
+ }, [setIsSystemUpgradeFormOpen]);
const closeSystemUpgradeForm = React.useCallback(() => {
- setSystemUpgradeFormOpen(false);
- }, [setSystemUpgradeFormOpen]);
+ setIsSystemUpgradeFormOpen(false);
+ }, [setIsSystemUpgradeFormOpen]);
if (systemUpgrades.length === 0) {
return (
@@ -59,9 +58,10 @@ export default function SystemUpgradeButton(props: Readonly<Props>) {
return (
<>
- <ButtonSecondary className="sw-ml-2" onClick={openSystemUpgradeForm}>
+ <Button className="sw-ml-2" onClick={openSystemUpgradeForm}>
{translate('learn_more')}
- </ButtonSecondary>
+ </Button>
+
{isSystemUpgradeFormOpen && (
<SystemUpgradeForm
onClose={closeSystemUpgradeForm}
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
index 73739e28939..f6f9dafa673 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
@@ -18,13 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Button, ButtonVariety, LinkStandalone, Modal } from '@sonarsource/echoes-react';
import { filter, flatMap, isEmpty, negate } from 'lodash';
-import { FlagMessage, Link, Modal } from '~design-system';
+import { FlagMessage } from '~design-system';
import { useAppState } from '../../app/components/app-state/withAppStateContext';
import { BANNER_VARIANT } from '../../app/components/update-notification/helpers';
import { translate } from '../../helpers/l10n';
+import { EditionKey } from '../../types/editions';
import { SystemUpgrade } from '../../types/system';
-import SystemUpgradeItem from './SystemUpgradeItem';
+import { SystemUpgradeItem } from './SystemUpgradeItem';
import { SYSTEM_VERSION_REGEXP, UpdateUseCase } from './utils';
interface Props {
@@ -34,15 +36,20 @@ interface Props {
updateUseCase: UpdateUseCase;
}
-export default function SystemUpgradeForm(props: Readonly<Props>) {
+export function SystemUpgradeForm(props: Readonly<Props>) {
const appState = useAppState();
+
const { latestLTA, onClose, updateUseCase, systemUpgrades } = props;
+ const isCommunityBuildRunning = appState.edition === EditionKey.community;
+
let systemUpgradesWithPatch: SystemUpgrade[][] = [];
+
const alertVariant =
updateUseCase !== UpdateUseCase.NewVersion ? BANNER_VARIANT[updateUseCase] : undefined;
- const header = translate('system.system_upgrade');
+
const parsedVersion = SYSTEM_VERSION_REGEXP.exec(appState.version);
+
let patches: SystemUpgrade[] = [];
if (updateUseCase === UpdateUseCase.NewPatch && parsedVersion !== null) {
@@ -52,6 +59,7 @@ export default function SystemUpgradeForm(props: Readonly<Props>) {
patches = flatMap(systemUpgrades, (upgrades) =>
filter(upgrades, (upgrade) => upgrade.version.startsWith(majoMinorVersion)),
);
+
systemUpgradesWithPatch = systemUpgrades
.map((upgrades) =>
upgrades.filter((upgrade) => !upgrade.version.startsWith(majoMinorVersion)),
@@ -65,6 +73,7 @@ export default function SystemUpgradeForm(props: Readonly<Props>) {
for (const upgrades of systemUpgrades) {
if (untilLTA === false) {
systemUpgradesWithPatch.push(upgrades);
+
untilLTA = upgrades.some(
(upgrade) => latestLTA !== undefined && upgrade.version.startsWith(latestLTA),
);
@@ -74,15 +83,14 @@ export default function SystemUpgradeForm(props: Readonly<Props>) {
return (
<Modal
- headerTitle={header}
- onClose={onClose}
- body={
- <>
+ content={
+ <div className="sw-mt-4">
{alertVariant && (
<FlagMessage variant={alertVariant} className={`it__upgrade-alert-${updateUseCase}`}>
{translate('admin_notification.update', updateUseCase)}
</FlagMessage>
)}
+
{systemUpgradesWithPatch.map((upgrades) => (
<SystemUpgradeItem
edition={appState.edition}
@@ -94,14 +102,39 @@ export default function SystemUpgradeForm(props: Readonly<Props>) {
)}
/>
))}
- </>
+ </div>
}
+ {...(isCommunityBuildRunning && {
+ description: translate(
+ 'admin_notification.update.new_sqs_version_when_running_sqcb.upgrade',
+ ),
+ })}
+ isOpen
+ onOpenChange={(isOpen) => {
+ if (!isOpen) {
+ onClose();
+ }
+ }}
primaryButton={
- <Link to="https://www.sonarsource.com/products/sonarqube/downloads/?referrer=sonarqube">
- {translate('system.see_sonarqube_downloads')}
- </Link>
+ !isCommunityBuildRunning && (
+ <LinkStandalone
+ className="sw-mr-8"
+ to="https://www.sonarsource.com/products/sonarqube/downloads/?referrer=sonarqube"
+ >
+ {translate('system.see_sonarqube_downloads')}
+ </LinkStandalone>
+ )
+ }
+ secondaryButton={
+ <Button onClick={onClose} variety={ButtonVariety.Default}>
+ {translate('cancel')}
+ </Button>
}
- secondaryButtonLabel={translate('cancel')}
+ title={translate(
+ isCommunityBuildRunning
+ ? 'admin_notification.update.new_sqs_version_when_running_sqcb.modal'
+ : 'system.system_upgrade',
+ )}
/>
);
}
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
index bc187305969..566a8177b6c 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
@@ -18,8 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Button, ButtonVariety, LinkStandalone } from '@sonarsource/echoes-react';
import { FormattedMessage } from 'react-intl';
-import { DownloadButton, Link, SubHeading } from '~design-system';
+import { DownloadButton, SubHeading } from '~design-system';
+import { useAppState } from '../../app/components/app-state/withAppStateContext';
import { DocLink } from '../../helpers/doc-links';
import {
getEdition,
@@ -28,7 +30,7 @@ import {
} from '../../helpers/editions';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { EditionKey } from '../../types/editions';
-import { SystemUpgrade } from '../../types/system';
+import { ProductName, SystemUpgrade } from '../../types/system';
import DocumentationLink from '../common/DocumentationLink';
import DateFormatter from '../intl/DateFormatter';
import SystemUpgradeIntermediate from './SystemUpgradeIntermediate';
@@ -40,14 +42,20 @@ export interface SystemUpgradeItemProps {
systemUpgrades: SystemUpgrade[];
}
-export default function SystemUpgradeItem(props: SystemUpgradeItemProps) {
+export function SystemUpgradeItem(props: Readonly<SystemUpgradeItemProps>) {
+ const appState = useAppState();
+
const { edition, isPatch, isLTAVersion, systemUpgrades } = props;
+
+ const isCommunityBuildRunning = appState.edition === EditionKey.community;
+
const lastUpgrade = systemUpgrades[0];
- const downloadUrl = getEditionDownloadUrl(
- getEdition(edition || EditionKey.community),
- lastUpgrade,
- );
+
+ const downloadUrl =
+ getEditionDownloadUrl(getEdition(edition ?? EditionKey.community), lastUpgrade) ?? '';
+
let header = translate('system.latest_version');
+
if (isLTAVersion) {
header = translate('system.lta_version');
} else if (isPatch) {
@@ -58,46 +66,78 @@ export default function SystemUpgradeItem(props: SystemUpgradeItemProps) {
<div className="system-upgrade-version it__upgrade-list-item">
<SubHeading as="h3">
<strong>{header}</strong>
+
{!isPatch && (
- <Link
+ <LinkStandalone
className="sw-ml-2"
to="https://www.sonarsource.com/products/sonarqube/whats-new/?referrer=sonarqube"
>
{translate('system.see_whats_new')}
- </Link>
+ </LinkStandalone>
)}
</SubHeading>
+
<p>
<FormattedMessage
- defaultMessage={translate('system.version_is_availble')}
id="system.version_is_availble"
- values={{ version: <b>SonarQube {lastUpgrade.version}</b> }}
+ values={{
+ version: (
+ <b>
+ {`${ProductName.SonarQubeServer} ${
+ isCommunityBuildRunning
+ ? lastUpgrade.version.split('.').join(' Release ')
+ : lastUpgrade.version
+ }`}
+ </b>
+ ),
+ }}
/>
</p>
+
<p className="sw-mt-2">{lastUpgrade.description}</p>
+
<div className="sw-mt-4">
- {lastUpgrade.releaseDate && (
+ {lastUpgrade.releaseDate !== undefined && (
<DateFormatter date={lastUpgrade.releaseDate} long>
{(formattedDate) => (
<span>{translateWithParameters('system.released_x', formattedDate)}</span>
)}
</DateFormatter>
)}
- {lastUpgrade.changeLogUrl && (
- <Link className="sw-ml-2" to={lastUpgrade.changeLogUrl}>
+
+ {lastUpgrade.changeLogUrl !== undefined && (
+ <LinkStandalone className="sw-ml-2" to={lastUpgrade.changeLogUrl}>
{translate('system.release_notes')}
- </Link>
+ </LinkStandalone>
)}
</div>
+
<SystemUpgradeIntermediate className="sw-mt-2" upgrades={systemUpgrades.slice(1)} />
+
<div className="sw-mt-4">
- <DownloadButton download={getEditionDownloadFilename(downloadUrl)} href={downloadUrl}>
- {translateWithParameters('system.download_x', lastUpgrade.version)}
- </DownloadButton>
+ {isCommunityBuildRunning ? (
+ <Button
+ // WARNING! A button acting as a link is bad a11y. We should replace this with a
+ // Call To Action (CTA) component from Echoes once it becomes available.
+ onClick={() => {
+ window.location.href = 'https://www.sonarsource.com/plans-and-pricing/sonarqube/';
+ }}
+ //
+ variety={ButtonVariety.Primary}
+ >
+ {translate('learn_more')}
+ </Button>
+ ) : (
+ <>
+ <DownloadButton download={getEditionDownloadFilename(downloadUrl)} href={downloadUrl}>
+ {translateWithParameters('system.download_x', lastUpgrade.version)}
+ </DownloadButton>
- <DocumentationLink className="sw-ml-2" to={DocLink.ServerUpgradeRoadmap}>
- {translate('system.how_to_upgrade')}
- </DocumentationLink>
+ <DocumentationLink className="sw-ml-4" to={DocLink.ServerUpgradeRoadmap}>
+ {translate('system.how_to_upgrade')}
+ </DocumentationLink>
+ </>
+ )}
</div>
</div>
);
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx
index dd19f764342..cd8e8d52ada 100644
--- a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx
@@ -23,7 +23,8 @@ import React from 'react';
import { byRole, byText } from '~sonar-aligned/helpers/testSelector';
import { mockAppState } from '../../../helpers/testMocks';
import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import SystemUpgradeButton from '../SystemUpgradeButton';
+import { EditionKey } from '../../../types/editions';
+import { SystemUpgradeButton } from '../SystemUpgradeButton';
import { UpdateUseCase } from '../utils';
const ui = {
@@ -85,6 +86,6 @@ function renderSystemUpgradeButton(
{...props}
/>,
'',
- { appState: mockAppState({ version }) },
+ { appState: mockAppState({ edition: EditionKey.developer, version }) },
);
}
diff --git a/server/sonar-web/src/main/js/design-system/sonar-aligned/components/buttons/ButtonSecondary.tsx b/server/sonar-web/src/main/js/design-system/sonar-aligned/components/buttons/ButtonSecondary.tsx
index 0a9945f10f0..971f3554e98 100644
--- a/server/sonar-web/src/main/js/design-system/sonar-aligned/components/buttons/ButtonSecondary.tsx
+++ b/server/sonar-web/src/main/js/design-system/sonar-aligned/components/buttons/ButtonSecondary.tsx
@@ -24,7 +24,7 @@ import { Button } from './Button';
/**
* @deprecated Use Button from Echoes instead without the `variety` prop set,
- * this is a the default look and feel of the button.
+ * this is the default look and feel of the button.
*
* Some of the props have changed or been renamed:
* - `blurAfterClick` is now `shouldBlurAfterClick`
diff --git a/server/sonar-web/src/main/js/helpers/l10nBundle.ts b/server/sonar-web/src/main/js/helpers/l10nBundle.ts
index d34b1f479bb..8c1ea4b2a1f 100644
--- a/server/sonar-web/src/main/js/helpers/l10nBundle.ts
+++ b/server/sonar-web/src/main/js/helpers/l10nBundle.ts
@@ -23,6 +23,7 @@ import { fetchL10nBundle } from '../api/l10n';
import { AppState } from '../types/appstate';
import { EditionKey } from '../types/editions';
import { L10nBundle, L10nBundleRequestParams } from '../types/l10nBundle';
+import { ProductName } from '../types/system';
import { Dict } from '../types/types';
import { toISO8601WithOffsetString } from './dates';
import { isDefined } from './types';
@@ -127,8 +128,8 @@ function persistL10nBundleInCache(bundle: L10nBundle) {
function getProductName(appState?: AppState) {
if (isDefined(appState?.edition)) {
return appState?.edition === EditionKey.community
- ? 'SonarQube Community Build'
- : 'SonarQube Server';
+ ? ProductName.SonarQubeCommunityBuild
+ : ProductName.SonarQubeServer;
}
return 'SonarQube';
diff --git a/server/sonar-web/src/main/js/helpers/mocks/system-upgrades.ts b/server/sonar-web/src/main/js/helpers/mocks/system-upgrades.ts
index 554245568a1..80466d15fce 100644
--- a/server/sonar-web/src/main/js/helpers/mocks/system-upgrades.ts
+++ b/server/sonar-web/src/main/js/helpers/mocks/system-upgrades.ts
@@ -18,15 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { SystemUpgrade } from '../../types/system';
+import { ProductNameForUpgrade, SystemUpgrade } from '../../types/system';
-export function mockSystemUpgrade(override: Partial<SystemUpgrade> = {}): SystemUpgrade {
- return {
- version: '5.6.7',
- description: 'Version 5.6.7 description',
- releaseDate: '2017-03-01',
+export const mockSystemUpgrade = (override: Partial<SystemUpgrade> = {}) =>
+ ({
changeLogUrl: 'changelogurl',
+ description: 'Version 5.6.7 description',
downloadUrl: 'downloadurl',
+ product: ProductNameForUpgrade.SonarQubeServer,
+ releaseDate: '2017-03-01',
+ version: '5.6.7',
...override,
- };
-}
+ }) as SystemUpgrade;
diff --git a/server/sonar-web/src/main/js/types/system.ts b/server/sonar-web/src/main/js/types/system.ts
index a91b1bfe4a6..64f57531325 100644
--- a/server/sonar-web/src/main/js/types/system.ts
+++ b/server/sonar-web/src/main/js/types/system.ts
@@ -25,9 +25,20 @@ export interface SystemUpgradeDownloadUrls {
downloadUrl: string;
}
+export enum ProductName {
+ SonarQubeCommunityBuild = 'SonarQube Community Build',
+ SonarQubeServer = 'SonarQube Server',
+}
+
+export enum ProductNameForUpgrade {
+ SonarQubeCommunityBuild = 'SONARQUBE_COMMUNITY_BUILD',
+ SonarQubeServer = 'SONARQUBE_SERVER',
+}
+
export interface SystemUpgrade extends SystemUpgradeDownloadUrls {
changeLogUrl?: string;
description?: string;
+ product?: ProductNameForUpgrade;
releaseDate?: string;
version: string;
}