]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15507 Add a prompt to warn when SonarQube need an update
authorMathieu Suen <mathieu.suen@sonarsource.com>
Thu, 14 Oct 2021 16:06:41 +0000 (18:06 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 21 Oct 2021 20:04:01 +0000 (20:04 +0000)
39 files changed:
server/sonar-web/src/main/js/api/system.ts
server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap
server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.css [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/update-notification/__tests__/UpdateNotification-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/system/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeForm.tsx [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeIntermediate.tsx [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeItem.tsx [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeNotif.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeForm-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeIntermediate-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeItem-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeNotif-test.tsx
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeForm-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeIntermediate-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeItem-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeNotif-test.tsx.snap
server/sonar-web/src/main/js/apps/system/utils.ts
server/sonar-web/src/main/js/components/ui/Alert.tsx
server/sonar-web/src/main/js/components/ui/DismissableAlert.tsx
server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/DismissableAlert-test.tsx.snap
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeButton-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeIntermediate-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeItem-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeButton-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeForm-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeIntermediate-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeItem-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/__tests__/utils-test.ts [new file with mode: 0644]
server/sonar-web/src/main/js/components/upgrade/utils.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/mocks/system-upgrades.ts [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 9adf55ea9dc1f199558b266325635550cd9b6c5a..7a613374ec34f95450e2d622a113f3cc81ab9924 100644 (file)
@@ -35,6 +35,7 @@ export function getSystemStatus(): Promise<{ id: string; version: string; status
 
 export function getSystemUpgrades(): Promise<{
   upgrades: SystemUpgrade[];
+  latestLTS: string;
   updateCenterRefresh: string;
 }> {
   return getJSON('/api/system/upgrades');
index 68763f336eae993d8cb419507e0599b582786afc..e31bf86e06618c4eaac08d38a359efce73730051 100644 (file)
@@ -28,6 +28,7 @@ import IndexationContextProvider from './indexation/IndexationContextProvider';
 import IndexationNotification from './indexation/IndexationNotification';
 import GlobalNav from './nav/global/GlobalNav';
 import StartupModal from './StartupModal';
+import UpdateNotification from './update-notification/UpdateNotification';
 
 export interface Props {
   children: React.ReactNode;
@@ -52,6 +53,7 @@ export default function GlobalContainer(props: Props) {
                     <GlobalNav location={props.location} />
                     <GlobalMessagesContainer />
                     <IndexationNotification />
+                    <UpdateNotification />
                     {props.children}
                   </IndexationContextProvider>
                 </Workspace>
index cf87ae0d4f2c55392a6f7ea9205043d84d37a811..7f04cba74c217a15f100cc9e2ba1229d5c108aef 100644 (file)
@@ -32,6 +32,7 @@ exports[`should render correctly 1`] = `
                 />
                 <Connect(GlobalMessages) />
                 <Connect(withCurrentUser(withIndexationContext(IndexationNotification))) />
+                <Connect(withCurrentUser(Connect(withAppState(UpdateNotification)))) />
                 <ChildComponent />
               </Connect(withAppState(IndexationContextProvider))>
             </Workspace>
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.css b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.css
new file mode 100644 (file)
index 0000000..fc23a2c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.
+ */
+.promote-update-notification.dismissable-alert-wrapper {
+  height: 42px;
+}
+
+.promote-update-notification .dismissable-alert-banner {
+  margin-bottom: 0 !important;
+  position: fixed;
+  width: 100%;
+  z-index: var(--globalBannerZIndex);
+}
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
new file mode 100644 (file)
index 0000000..1a3a23b
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 { groupBy, isEmpty, mapValues } from 'lodash';
+import * as React from 'react';
+import { getSystemUpgrades } from '../../../api/system';
+import { withAppState } from '../../../components/hoc/withAppState';
+import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import { AlertVariant } from '../../../components/ui/Alert';
+import DismissableAlert from '../../../components/ui/DismissableAlert';
+import SystemUpgradeButton from '../../../components/upgrade/SystemUpgradeButton';
+import { sortUpgrades } from '../../../components/upgrade/utils';
+import { translate } from '../../../helpers/l10n';
+import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
+import { Permissions } from '../../../types/permissions';
+import { SystemUpgrade } from '../../../types/system';
+import './UpdateNotification.css';
+
+const MONTH_BEFOR_PREVIOUS_LTS_NOTIFICATION = 6;
+
+type GroupedSystemUpdate = {
+  [x: string]: T.Dict<SystemUpgrade[]>;
+};
+
+enum UseCase {
+  NewMinorVersion = 'new_minor_version',
+  NewPatch = 'new_patch',
+  PreLTS = 'pre_lts',
+  PreviousLTS = 'previous_lts'
+}
+
+const MAP_VARIANT: T.Dict<AlertVariant> = {
+  [UseCase.NewMinorVersion]: 'info',
+  [UseCase.NewPatch]: 'warning',
+  [UseCase.PreLTS]: 'warning',
+  [UseCase.PreviousLTS]: 'error'
+};
+
+interface Props {
+  appState: Pick<T.AppState, 'version'>;
+  currentUser: T.CurrentUser;
+}
+
+interface State {
+  dismissKey: string;
+  useCase: UseCase;
+  systemUpgrades: SystemUpgrade[];
+  canSeeNotification: boolean;
+}
+
+export class UpdateNotification extends React.PureComponent<Props, State> {
+  mounted = false;
+  versionParser = /^(\d+)\.(\d+)(\.(\d+))?/;
+
+  constructor(props: Props) {
+    super(props);
+    this.state = {
+      dismissKey: '',
+      systemUpgrades: [],
+      canSeeNotification: false,
+      useCase: UseCase.NewMinorVersion
+    };
+    this.fetchSystemUpgradeInformation();
+  }
+
+  componentDidMount() {
+    this.mounted = true;
+  }
+
+  componentDidUpdate(prevProps: Props) {
+    if (
+      prevProps.currentUser !== this.props.currentUser ||
+      this.props.appState.version !== prevProps.appState.version
+    ) {
+      this.fetchSystemUpgradeInformation();
+    }
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  isPreLTSUpdate(parsedVersion: number[], latestLTS: string) {
+    const [currentMajor, currentMinor] = parsedVersion;
+    const [ltsMajor, ltsMinor] = latestLTS.split('.').map(Number);
+    return currentMajor < ltsMajor || (currentMajor === ltsMajor && currentMinor < ltsMinor);
+  }
+
+  isPreviousLTSUpdate(
+    parsedVersion: number[],
+    latestLTS: string,
+    systemUpgrades: GroupedSystemUpdate
+  ) {
+    const [ltsMajor, ltsMinor] = latestLTS.split('.').map(Number);
+    let ltsOlderThan6Month = false;
+    const beforeLts = this.isPreLTSUpdate(parsedVersion, latestLTS);
+    if (beforeLts) {
+      const allLTS = sortUpgrades(systemUpgrades[ltsMajor][ltsMinor]);
+      const ltsReleaseDate = new Date(allLTS[allLTS.length - 1]?.releaseDate || '');
+      if (isNaN(ltsReleaseDate.getTime())) {
+        // We can not parse the LTS date.
+        // It is unlikly that this could happen but consider LTS to be old.
+        return true;
+      }
+      ltsOlderThan6Month =
+        ltsReleaseDate.setMonth(ltsReleaseDate.getMonth() + MONTH_BEFOR_PREVIOUS_LTS_NOTIFICATION) -
+          Date.now() <
+        0;
+    }
+    return ltsOlderThan6Month && beforeLts;
+  }
+
+  isMinorUpdate(parsedVersion: number[], systemUpgrades: GroupedSystemUpdate) {
+    const [currentMajor, currentMinor] = parsedVersion;
+    const allMinor = systemUpgrades[currentMajor];
+    return Object.keys(allMinor)
+      .map(Number)
+      .some(minor => minor > currentMinor);
+  }
+
+  isPatchUpdate(parsedVersion: number[], systemUpgrades: GroupedSystemUpdate) {
+    const [currentMajor, currentMinor, currentPatch] = parsedVersion;
+    const allMinor = systemUpgrades[currentMajor];
+    const allPatch = sortUpgrades(allMinor[currentMinor] || []);
+
+    if (!isEmpty(allPatch)) {
+      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;
+  }
+
+  async fetchSystemUpgradeInformation() {
+    if (
+      !isLoggedIn(this.props.currentUser) ||
+      !hasGlobalPermission(this.props.currentUser, Permissions.Admin)
+    ) {
+      this.noPromptToShow();
+      return;
+    }
+
+    const regExpParsedVersion = this.versionParser.exec(this.props.appState.version);
+    if (regExpParsedVersion === null) {
+      this.noPromptToShow();
+      return;
+    }
+    regExpParsedVersion.shift();
+    const parsedVersion = regExpParsedVersion.map(Number).map(n => (isNaN(n) ? 0 : n));
+
+    const { upgrades, latestLTS } = await getSystemUpgrades();
+
+    if (isEmpty(upgrades)) {
+      // No new upgrades
+      this.noPromptToShow();
+      return;
+    }
+    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 = UseCase.NewMinorVersion;
+
+    if (this.isPreviousLTSUpdate(parsedVersion, latestLTS, systemUpgrades)) {
+      useCase = UseCase.PreviousLTS;
+    } else if (this.isPreLTSUpdate(parsedVersion, latestLTS)) {
+      useCase = UseCase.PreLTS;
+    } else if (this.isPatchUpdate(parsedVersion, systemUpgrades)) {
+      useCase = UseCase.NewPatch;
+    } else if (this.isMinorUpdate(parsedVersion, systemUpgrades)) {
+      useCase = UseCase.NewMinorVersion;
+    }
+
+    const latest = [...upgrades].sort(
+      (upgrade1, upgrade2) =>
+        new Date(upgrade2.releaseDate || '').getTime() -
+        new Date(upgrade1.releaseDate || '').getTime()
+    )[0];
+
+    const dismissKey = useCase + latest.version;
+
+    if (this.mounted) {
+      this.setState({
+        useCase,
+        dismissKey,
+        systemUpgrades: upgrades,
+        canSeeNotification: true
+      });
+    }
+  }
+
+  noPromptToShow() {
+    if (this.mounted) {
+      this.setState({ canSeeNotification: false });
+    }
+  }
+
+  render() {
+    const { systemUpgrades, canSeeNotification, useCase, dismissKey } = this.state;
+    if (!canSeeNotification) {
+      return null;
+    }
+    return (
+      <DismissableAlert
+        alertKey={dismissKey}
+        variant={MAP_VARIANT[useCase]}
+        className="promote-update-notification">
+        {translate('admin_notification.update', useCase)}
+        <SystemUpgradeButton systemUpgrades={systemUpgrades} />
+      </DismissableAlert>
+    );
+  }
+}
+
+export default withCurrentUser(withAppState(UpdateNotification));
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/__tests__/UpdateNotification-test.tsx b/server/sonar-web/src/main/js/app/components/update-notification/__tests__/UpdateNotification-test.tsx
new file mode 100644 (file)
index 0000000..6c284a0
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { getSystemUpgrades } from '../../../../api/system';
+import DismissableAlert from '../../../../components/ui/DismissableAlert';
+import { mockUpgrades } from '../../../../helpers/mocks/system-upgrades';
+import { mockAppState, mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { Permissions } from '../../../../types/permissions';
+import { UpdateNotification } from '../UpdateNotification';
+
+jest.mock('../../../../api/system', () => {
+  const { mockUpgrades } = jest.requireActual('../../../../helpers/mocks/system-upgrades');
+  return {
+    getSystemUpgrades: jest.fn().mockResolvedValue({ upgrades: [mockUpgrades()], latestLTS: '8.9' })
+  };
+});
+
+function formatDate(date: Date): string {
+  return `${date.getFullYear()}-${date.getMonth()}-${date.getDay()}`;
+}
+
+it('should not show prompt when not admin', async () => {
+  //As anonymous
+  const wrapper = shallowRender();
+  await waitAndUpdate(wrapper);
+  expect(wrapper.type()).toBeNull();
+
+  // As non admin user
+  wrapper.setProps({ currentUser: mockLoggedInUser() });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.type()).toBeNull();
+});
+
+it('should not show prompt when no current version', async () => {
+  const wrapper = shallowRender({ appState: mockAppState({ version: 'NOVERSION' }) });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.type()).toBeNull();
+});
+
+it('should not show prompt when no upgrade', async () => {
+  (getSystemUpgrades as jest.Mock).mockResolvedValueOnce({ upgrades: [], latestLTS: '8.9' });
+  const wrapper = shallowRender({
+    appState: mockAppState({ version: '9.1' }),
+    currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })
+  });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.type()).toBeNull();
+});
+
+it('should show prompt when no lts date', async () => {
+  (getSystemUpgrades as jest.Mock).mockResolvedValueOnce({
+    upgrades: [mockUpgrades({ version: '8.9', releaseDate: 'INVALID' })],
+    latestLTS: '8.9'
+  });
+  const wrapper = shallowRender({
+    appState: mockAppState({ version: '8.1' }),
+    currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })
+  });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.find(DismissableAlert).props().alertKey).toBe('previous_lts8.9');
+  expect(wrapper.contains('admin_notification.update.previous_lts')).toBe(true);
+});
+
+it('should show prompt when minor upgrade', async () => {
+  (getSystemUpgrades as jest.Mock).mockResolvedValueOnce({
+    upgrades: [mockUpgrades({ version: '9.2' }), mockUpgrades({ version: '9.1' })],
+    latestLTS: '8.9'
+  });
+  const wrapper = shallowRender({
+    appState: mockAppState({ version: '9.1' }),
+    currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })
+  });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.find(DismissableAlert).props().alertKey).toBe('new_minor_version9.2');
+  expect(wrapper.contains('admin_notification.update.new_minor_version')).toBe(true);
+});
+
+it('should show prompt when patch upgrade', async () => {
+  (getSystemUpgrades as jest.Mock).mockResolvedValueOnce({
+    upgrades: [mockUpgrades({ version: '9.2' }), mockUpgrades({ version: '9.1.1' })],
+    latestLTS: '8.9'
+  });
+  const wrapper = shallowRender({
+    appState: mockAppState({ version: '9.1' }),
+    currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })
+  });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.find(DismissableAlert).props().alertKey).toBe('new_patch9.2');
+  expect(wrapper.contains('admin_notification.update.new_patch')).toBe(true);
+});
+
+it('should show prompt when lts upgrade', async () => {
+  (getSystemUpgrades as jest.Mock).mockResolvedValueOnce({
+    upgrades: [
+      mockUpgrades({ version: '8.9', releaseDate: formatDate(new Date(Date.now())) }),
+      mockUpgrades({ version: '9.2' }),
+      mockUpgrades({ version: '9.1.1' })
+    ],
+    latestLTS: '8.9'
+  });
+  const wrapper = shallowRender({
+    appState: mockAppState({ version: '8.8' }),
+    currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })
+  });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.find(DismissableAlert).props().alertKey).toBe('pre_lts8.9');
+  expect(wrapper.contains('admin_notification.update.pre_lts')).toBe(true);
+});
+
+it('should show prompt when lts upgrade is more than 6 month', async () => {
+  const ltsDate = new Date(Date.now());
+  ltsDate.setMonth(ltsDate.getMonth() - 7);
+  (getSystemUpgrades as jest.Mock).mockResolvedValueOnce({
+    upgrades: [
+      mockUpgrades({ version: '8.9', releaseDate: formatDate(ltsDate) }),
+      mockUpgrades({ version: '9.2' }),
+      mockUpgrades({ version: '9.1.1' })
+    ],
+    latestLTS: '8.9'
+  });
+  const wrapper = shallowRender({
+    appState: mockAppState({ version: '8.8' }),
+    currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Admin] } })
+  });
+  await waitAndUpdate(wrapper);
+  expect(wrapper.find(DismissableAlert).props().alertKey).toBe('previous_lts8.9');
+  expect(wrapper.contains('admin_notification.update.previous_lts')).toBe(true);
+});
+
+function shallowRender(props: Partial<UpdateNotification['props']> = {}) {
+  return shallow(
+    <UpdateNotification appState={mockAppState()} currentUser={mockCurrentUser()} {...props} />
+  );
+}
index 331497b902d76ee94e3374da7dc62b17538c4170..3194894a6ab27c0e008c2cd45c38abba1a991fa6 100644 (file)
@@ -19,7 +19,6 @@
  */
 
 import { mockClusterSysInfo, mockStandaloneSysInfo } from '../../../helpers/testMocks';
-import { SystemUpgrade } from '../../../types/system';
 import * as u from '../utils';
 
 describe('parseQuery', () => {
@@ -73,54 +72,6 @@ describe('getSystemLogsLevel', () => {
   });
 });
 
-describe('sortUpgrades', () => {
-  it('should sort correctly versions', () => {
-    expect(
-      u.sortUpgrades([
-        { version: '5.4.2' },
-        { version: '5.10' },
-        { version: '5.1' },
-        { version: '5.4' }
-      ] as SystemUpgrade[])
-    ).toEqual([{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]);
-    expect(
-      u.sortUpgrades([
-        { version: '5.10' },
-        { version: '5.1.2' },
-        { version: '6.0' },
-        { version: '6.9' }
-      ] as SystemUpgrade[])
-    ).toEqual([{ version: '6.9' }, { version: '6.0' }, { version: '5.10' }, { version: '5.1.2' }]);
-  });
-});
-
-describe('groupUpgrades', () => {
-  it('should group correctly', () => {
-    expect(
-      u.groupUpgrades([
-        { version: '5.10' },
-        { version: '5.4.2' },
-        { version: '5.4' },
-        { version: '5.1' }
-      ] as SystemUpgrade[])
-    ).toEqual([
-      [{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]
-    ]);
-    expect(
-      u.groupUpgrades([
-        { version: '6.9' },
-        { version: '6.7' },
-        { version: '6.0' },
-        { version: '5.10' },
-        { version: '5.4.2' }
-      ] as SystemUpgrade[])
-    ).toEqual([
-      [{ version: '6.9' }, { version: '6.7' }, { version: '6.0' }],
-      [{ version: '5.10' }, { version: '5.4.2' }]
-    ]);
-  });
-});
-
 describe('isCluster', () => {
   it('should return the correct information', () => {
     expect(u.isCluster(mockClusterSysInfo())).toBe(true);
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeForm.tsx b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeForm.tsx
deleted file mode 100644 (file)
index b4a25a7..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { ResetButtonLink } from '../../../../components/controls/buttons';
-import Modal from '../../../../components/controls/Modal';
-import { withAppState } from '../../../../components/hoc/withAppState';
-import { translate } from '../../../../helpers/l10n';
-import { EditionKey } from '../../../../types/editions';
-import { SystemUpgrade } from '../../../../types/system';
-import SystemUpgradeItem from './SystemUpgradeItem';
-
-interface Props {
-  appState: Pick<T.AppState, 'edition'>;
-  onClose: () => void;
-  systemUpgrades: SystemUpgrade[][];
-}
-
-interface State {
-  upgrading: boolean;
-}
-
-export class SystemUpgradeForm extends React.PureComponent<Props, State> {
-  state: State = { upgrading: false };
-
-  render() {
-    const { upgrading } = this.state;
-    const { appState, systemUpgrades } = this.props;
-    const header = translate('system.system_upgrade');
-    return (
-      <Modal contentLabel={header} onRequestClose={this.props.onClose}>
-        <div className="modal-head">
-          <h2>{header}</h2>
-        </div>
-        <div className="modal-body">
-          {systemUpgrades.map((upgrades, idx) => (
-            <SystemUpgradeItem
-              edition={
-                appState.edition as EditionKey /* TODO: Fix once AppState is no longer ambiant. */
-              }
-              key={upgrades[upgrades.length - 1].version}
-              systemUpgrades={upgrades}
-              isLatestVersion={idx === 0}
-            />
-          ))}
-        </div>
-        <div className="modal-foot">
-          {upgrading && <i className="spinner spacer-right" />}
-          <a
-            className="pull-left"
-            href="https://www.sonarqube.org/downloads/?referrer=sonarqube"
-            rel="noopener noreferrer"
-            target="_blank">
-            {translate('system.see_sonarqube_downloads')}
-          </a>
-          <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
-        </div>
-      </Modal>
-    );
-  }
-}
-
-export default withAppState(SystemUpgradeForm);
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeIntermediate.tsx b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeIntermediate.tsx
deleted file mode 100644 (file)
index b059fe4..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { ButtonLink } from '../../../../components/controls/buttons';
-import DropdownIcon from '../../../../components/icons/DropdownIcon';
-import DateFormatter from '../../../../components/intl/DateFormatter';
-import { translate } from '../../../../helpers/l10n';
-import { SystemUpgrade } from '../../../../types/system';
-
-interface Props {
-  className?: string;
-  upgrades: SystemUpgrade[];
-}
-
-interface State {
-  showMore: boolean;
-}
-
-export default class SystemUpgradeIntermediate extends React.PureComponent<Props, State> {
-  state: State = { showMore: false };
-
-  toggleIntermediatVersions = () => {
-    this.setState(state => ({ showMore: !state.showMore }));
-  };
-
-  render() {
-    const { showMore } = this.state;
-    const { upgrades } = this.props;
-    if (upgrades.length <= 0) {
-      return null;
-    }
-
-    return (
-      <div className={this.props.className}>
-        <ButtonLink className="little-spacer-bottom" onClick={this.toggleIntermediatVersions}>
-          {showMore
-            ? translate('system.hide_intermediate_versions')
-            : translate('system.show_intermediate_versions')}
-          <DropdownIcon className="little-spacer-left" turned={showMore} />
-        </ButtonLink>
-        {showMore &&
-          upgrades.map(upgrade => (
-            <div className="note system-upgrade-intermediate" key={upgrade.version}>
-              {upgrade.releaseDate && (
-                <DateFormatter date={upgrade.releaseDate} long={true}>
-                  {formattedDate => (
-                    <p>
-                      <b className="little-spacer-right">SonarQube {upgrade.version}</b>
-                      {formattedDate}
-                      {upgrade.changeLogUrl && (
-                        <a
-                          className="spacer-left"
-                          href={upgrade.changeLogUrl}
-                          rel="noopener noreferrer"
-                          target="_blank">
-                          {translate('system.release_notes')}
-                        </a>
-                      )}
-                    </p>
-                  )}
-                </DateFormatter>
-              )}
-              {upgrade.description && <p className="little-spacer-top">{upgrade.description}</p>}
-            </div>
-          ))}
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeItem.tsx b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/SystemUpgradeItem.tsx
deleted file mode 100644 (file)
index d5c605c..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import DateFormatter from '../../../../components/intl/DateFormatter';
-import {
-  getEdition,
-  getEditionDownloadFilename,
-  getEditionDownloadUrl
-} from '../../../../helpers/editions';
-import { translate, translateWithParameters } from '../../../../helpers/l10n';
-import { EditionKey } from '../../../../types/editions';
-import { SystemUpgrade } from '../../../../types/system';
-import SystemUpgradeIntermediate from './SystemUpgradeIntermediate';
-
-export interface SystemUpgradeItemProps {
-  edition: EditionKey | undefined;
-  isLatestVersion: boolean;
-  systemUpgrades: SystemUpgrade[];
-}
-
-export default function SystemUpgradeItem(props: SystemUpgradeItemProps) {
-  const { edition, isLatestVersion, systemUpgrades } = props;
-  const lastUpgrade = systemUpgrades[0];
-  const downloadUrl = getEditionDownloadUrl(
-    getEdition(edition || EditionKey.community),
-    lastUpgrade
-  );
-
-  return (
-    <div className="system-upgrade-version">
-      <h3 className="h1 spacer-bottom">
-        <strong>
-          {isLatestVersion ? translate('system.latest_version') : translate('system.lts_version')}
-        </strong>
-        {isLatestVersion && (
-          <a
-            className="spacer-left medium"
-            href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
-            rel="noopener noreferrer"
-            target="_blank">
-            {translate('system.see_whats_new')}
-          </a>
-        )}
-      </h3>
-      <p>
-        <FormattedMessage
-          defaultMessage={translate('system.version_is_availble')}
-          id="system.version_is_availble"
-          values={{ version: <b>SonarQube {lastUpgrade.version}</b> }}
-        />
-      </p>
-      <p className="spacer-top">{lastUpgrade.description}</p>
-      <div className="big-spacer-top">
-        {lastUpgrade.releaseDate && (
-          <DateFormatter date={lastUpgrade.releaseDate} long={true}>
-            {formattedDate => (
-              <span>{translateWithParameters('system.released_x', formattedDate)}</span>
-            )}
-          </DateFormatter>
-        )}
-        {lastUpgrade.changeLogUrl && (
-          <a
-            className="spacer-left"
-            href={lastUpgrade.changeLogUrl}
-            rel="noopener noreferrer"
-            target="_blank">
-            {translate('system.release_notes')}
-          </a>
-        )}
-      </div>
-      <SystemUpgradeIntermediate className="spacer-top" upgrades={systemUpgrades.slice(1)} />
-      <div className="big-spacer-top">
-        <a
-          className="button"
-          download={getEditionDownloadFilename(downloadUrl)}
-          href={downloadUrl}
-          rel="noopener noreferrer"
-          target="_blank">
-          {translateWithParameters('system.download_x', lastUpgrade.version)}
-        </a>
-        <a
-          className="spacer-left"
-          href="https://redirect.sonarsource.com/doc/upgrading.html"
-          rel="noopener noreferrer"
-          target="_blank">
-          {translate('system.how_to_upgrade')}
-        </a>
-      </div>
-    </div>
-  );
-}
index 5655488d6d83522d07d256ea99365b479c9f9006..e6f7c4c090326c0733372b248cf153ff84b74290 100644 (file)
  */
 import * as React from 'react';
 import { getSystemUpgrades } from '../../../../api/system';
-import { Button } from '../../../../components/controls/buttons';
 import { Alert } from '../../../../components/ui/Alert';
+import SystemUpgradeButton from '../../../../components/upgrade/SystemUpgradeButton';
 import { translate } from '../../../../helpers/l10n';
 import { SystemUpgrade } from '../../../../types/system';
-import { groupUpgrades, sortUpgrades } from '../../utils';
-import SystemUpgradeForm from './SystemUpgradeForm';
 
 interface State {
-  systemUpgrades: SystemUpgrade[][];
-  openSystemUpgradeForm: boolean;
+  systemUpgrades: SystemUpgrade[];
 }
 
 export default class SystemUpgradeNotif extends React.PureComponent<{}, State> {
   mounted = false;
-  state: State = { openSystemUpgradeForm: false, systemUpgrades: [] };
+  state: State = { systemUpgrades: [] };
 
   componentDidMount() {
     this.mounted = true;
@@ -48,20 +45,12 @@ export default class SystemUpgradeNotif extends React.PureComponent<{}, State> {
     getSystemUpgrades().then(
       ({ upgrades }) => {
         if (this.mounted) {
-          this.setState({ systemUpgrades: groupUpgrades(sortUpgrades(upgrades)) });
+          this.setState({ systemUpgrades: upgrades });
         }
       },
       () => {}
     );
 
-  handleOpenSystemUpgradeForm = () => {
-    this.setState({ openSystemUpgradeForm: true });
-  };
-
-  handleCloseSystemUpgradeForm = () => {
-    this.setState({ openSystemUpgradeForm: false });
-  };
-
   render() {
     const { systemUpgrades } = this.state;
 
@@ -73,16 +62,8 @@ export default class SystemUpgradeNotif extends React.PureComponent<{}, State> {
       <div className="page-notifs">
         <Alert variant="info">
           {translate('system.new_version_available')}
-          <Button className="spacer-left" onClick={this.handleOpenSystemUpgradeForm}>
-            {translate('learn_more')}
-          </Button>
+          <SystemUpgradeButton systemUpgrades={systemUpgrades} />
         </Alert>
-        {this.state.openSystemUpgradeForm && (
-          <SystemUpgradeForm
-            onClose={this.handleCloseSystemUpgradeForm}
-            systemUpgrades={systemUpgrades}
-          />
-        )}
       </div>
     );
   }
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeForm-test.tsx b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeForm-test.tsx
deleted file mode 100644 (file)
index 02bf8a1..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { EditionKey } from '../../../../../types/editions';
-import { SystemUpgradeForm } from '../SystemUpgradeForm';
-
-const UPGRADES = [
-  [
-    {
-      version: '6.4',
-      description: 'Version 6.4 description',
-      releaseDate: '2017-06-02',
-      changeLogUrl: 'changelogurl',
-      downloadUrl: 'downloadurl',
-      plugins: {}
-    },
-    {
-      version: '6.3',
-      description: 'Version 6.3 description',
-      releaseDate: '2017-05-02',
-      changeLogUrl: 'changelogurl',
-      downloadUrl: 'downloadurl',
-      plugins: {}
-    }
-  ],
-  [
-    {
-      version: '5.6.7',
-      description: 'Version 5.6.7 description',
-      releaseDate: '2017-03-01',
-      changeLogUrl: 'changelogurl',
-      downloadUrl: 'downloadurl',
-      plugins: {}
-    },
-    {
-      version: '5.6.6',
-      description: 'Version 5.6.6 description',
-      releaseDate: '2017-04-02',
-      changeLogUrl: 'changelogurl',
-      downloadUrl: 'downloadurl',
-      plugins: {}
-    },
-    {
-      version: '5.6.5',
-      description: 'Version 5.6.5 description',
-      releaseDate: '2017-03-01',
-      changeLogUrl: 'changelogurl',
-      downloadUrl: 'downloadurl',
-      plugins: {}
-    }
-  ]
-];
-
-it('should display correctly', () => {
-  expect(
-    shallow(
-      <SystemUpgradeForm
-        appState={{ edition: EditionKey.community }}
-        onClose={jest.fn()}
-        systemUpgrades={UPGRADES}
-      />
-    )
-  ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeIntermediate-test.tsx b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeIntermediate-test.tsx
deleted file mode 100644 (file)
index c2d05b1..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { click } from '../../../../../helpers/testUtils';
-import SystemUpgradeIntermediate from '../SystemUpgradeIntermediate';
-
-const UPGRADES = [
-  {
-    version: '5.6.6',
-    description: 'Version 5.6.6 description',
-    releaseDate: '2017-04-02',
-    changeLogUrl: 'changelogurl',
-    downloadUrl: 'downloadurl',
-    plugins: {}
-  },
-  {
-    version: '5.6.5',
-    description: 'Version 5.6.5 description',
-    releaseDate: '2017-03-01',
-    changeLogUrl: 'changelogurl',
-    downloadUrl: 'downloadurl',
-    plugins: {}
-  }
-];
-
-it('should display correctly', () => {
-  const wrapper = shallow(<SystemUpgradeIntermediate upgrades={UPGRADES} />);
-  expect(wrapper).toMatchSnapshot();
-  wrapper.setState({ showMore: true });
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should allow to show and hide intermediates', () => {
-  const wrapper = shallow(<SystemUpgradeIntermediate upgrades={UPGRADES} />);
-  expect(wrapper.find('.system-upgrade-intermediate').exists()).toBe(false);
-  click(wrapper.find('ButtonLink'));
-  expect(wrapper.find('.system-upgrade-intermediate').exists()).toBe(true);
-  click(wrapper.find('ButtonLink'));
-  expect(wrapper.find('.system-upgrade-intermediate').exists()).toBe(false);
-});
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeItem-test.tsx b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeItem-test.tsx
deleted file mode 100644 (file)
index 6b775f0..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { EditionKey } from '../../../../../types/editions';
-import SystemUpgradeItem, { SystemUpgradeItemProps } from '../SystemUpgradeItem';
-
-it('should display correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-  expect(shallowRender({ isLatestVersion: false })).toMatchSnapshot();
-  expect(shallowRender({ edition: EditionKey.developer })).toMatchSnapshot();
-  expect(shallowRender({ edition: EditionKey.enterprise })).toMatchSnapshot();
-  expect(shallowRender({ edition: EditionKey.datacenter })).toMatchSnapshot();
-  // Fallback to Community.
-  expect(
-    shallowRender({
-      systemUpgrades: [
-        {
-          version: '5.6.7',
-          description: 'Version 5.6.7 description',
-          releaseDate: '2017-03-01',
-          changeLogUrl: 'http://changelog.url/',
-          downloadUrl: 'http://download.url/community'
-        }
-      ]
-    })
-  ).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<SystemUpgradeItemProps> = {}) {
-  return shallow<SystemUpgradeItemProps>(
-    <SystemUpgradeItem
-      edition={EditionKey.community}
-      systemUpgrades={[
-        {
-          version: '5.6.7',
-          description: 'Version 5.6.7 description',
-          releaseDate: '2017-03-01',
-          changeLogUrl: 'http://changelog.url/',
-          downloadUrl: 'http://download.url/community',
-          downloadDeveloperUrl: 'http://download.url/developer',
-          downloadEnterpriseUrl: 'http://download.url/enterprise',
-          downloadDatacenterUrl: 'http://download.url/datacenter'
-        },
-        {
-          version: '5.6.6',
-          description: 'Version 5.6.6 description',
-          releaseDate: '2017-04-02',
-          changeLogUrl: 'http://changelog.url/',
-          downloadUrl: 'http://download.url/community',
-          downloadDeveloperUrl: 'http://download.url/developer'
-        },
-        {
-          version: '5.6.5',
-          description: 'Version 5.6.5 description',
-          releaseDate: '2017-03-01',
-          changeLogUrl: 'http://changelog.url/',
-          downloadUrl: 'http://download.url/community',
-          downloadDeveloperUrl: 'http://download.url/developer'
-        }
-      ]}
-      isLatestVersion={true}
-      {...props}
-    />
-  );
-}
index 7ad1e031157d14ed381bb5ae2f6bfe2535ce556f..61673af52a250990a337b29b777b312ef0d604d8 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { getSystemUpgrades } from '../../../../../api/system';
-import { click, waitAndUpdate } from '../../../../../helpers/testUtils';
+import { waitAndUpdate } from '../../../../../helpers/testUtils';
 import SystemUpgradeNotif from '../SystemUpgradeNotif';
 
 jest.mock('../../../../../api/system', () => ({
@@ -83,9 +83,6 @@ it('should render correctly', async () => {
   expect(getSystemUpgrades).toHaveBeenCalled();
 
   expect(wrapper).toMatchSnapshot();
-
-  click(wrapper.find('Button'));
-  expect(wrapper).toMatchSnapshot();
 });
 
 it('should display nothing', async () => {
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeForm-test.tsx.snap
deleted file mode 100644 (file)
index e1c8041..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display correctly 1`] = `
-<Modal
-  contentLabel="system.system_upgrade"
-  onRequestClose={[MockFunction]}
->
-  <div
-    className="modal-head"
-  >
-    <h2>
-      system.system_upgrade
-    </h2>
-  </div>
-  <div
-    className="modal-body"
-  >
-    <SystemUpgradeItem
-      edition="community"
-      isLatestVersion={true}
-      key="6.3"
-      systemUpgrades={
-        Array [
-          Object {
-            "changeLogUrl": "changelogurl",
-            "description": "Version 6.4 description",
-            "downloadUrl": "downloadurl",
-            "plugins": Object {},
-            "releaseDate": "2017-06-02",
-            "version": "6.4",
-          },
-          Object {
-            "changeLogUrl": "changelogurl",
-            "description": "Version 6.3 description",
-            "downloadUrl": "downloadurl",
-            "plugins": Object {},
-            "releaseDate": "2017-05-02",
-            "version": "6.3",
-          },
-        ]
-      }
-    />
-    <SystemUpgradeItem
-      edition="community"
-      isLatestVersion={false}
-      key="5.6.5"
-      systemUpgrades={
-        Array [
-          Object {
-            "changeLogUrl": "changelogurl",
-            "description": "Version 5.6.7 description",
-            "downloadUrl": "downloadurl",
-            "plugins": Object {},
-            "releaseDate": "2017-03-01",
-            "version": "5.6.7",
-          },
-          Object {
-            "changeLogUrl": "changelogurl",
-            "description": "Version 5.6.6 description",
-            "downloadUrl": "downloadurl",
-            "plugins": Object {},
-            "releaseDate": "2017-04-02",
-            "version": "5.6.6",
-          },
-          Object {
-            "changeLogUrl": "changelogurl",
-            "description": "Version 5.6.5 description",
-            "downloadUrl": "downloadurl",
-            "plugins": Object {},
-            "releaseDate": "2017-03-01",
-            "version": "5.6.5",
-          },
-        ]
-      }
-    />
-  </div>
-  <div
-    className="modal-foot"
-  >
-    <a
-      className="pull-left"
-      href="https://www.sonarqube.org/downloads/?referrer=sonarqube"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.see_sonarqube_downloads
-    </a>
-    <ResetButtonLink
-      onClick={[MockFunction]}
-    >
-      cancel
-    </ResetButtonLink>
-  </div>
-</Modal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeIntermediate-test.tsx.snap b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeIntermediate-test.tsx.snap
deleted file mode 100644 (file)
index 763ab35..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display correctly 1`] = `
-<div>
-  <ButtonLink
-    className="little-spacer-bottom"
-    onClick={[Function]}
-  >
-    system.show_intermediate_versions
-    <DropdownIcon
-      className="little-spacer-left"
-      turned={false}
-    />
-  </ButtonLink>
-</div>
-`;
-
-exports[`should display correctly 2`] = `
-<div>
-  <ButtonLink
-    className="little-spacer-bottom"
-    onClick={[Function]}
-  >
-    system.hide_intermediate_versions
-    <DropdownIcon
-      className="little-spacer-left"
-      turned={true}
-    />
-  </ButtonLink>
-  <div
-    className="note system-upgrade-intermediate"
-    key="5.6.6"
-  >
-    <DateFormatter
-      date="2017-04-02"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <p
-      className="little-spacer-top"
-    >
-      Version 5.6.6 description
-    </p>
-  </div>
-  <div
-    className="note system-upgrade-intermediate"
-    key="5.6.5"
-  >
-    <DateFormatter
-      date="2017-03-01"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <p
-      className="little-spacer-top"
-    >
-      Version 5.6.5 description
-    </p>
-  </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeItem-test.tsx.snap
deleted file mode 100644 (file)
index 51d35d9..0000000
+++ /dev/null
@@ -1,592 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display correctly 1`] = `
-<div
-  className="system-upgrade-version"
->
-  <h3
-    className="h1 spacer-bottom"
-  >
-    <strong>
-      system.latest_version
-    </strong>
-    <a
-      className="spacer-left medium"
-      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.see_whats_new
-    </a>
-  </h3>
-  <p>
-    <FormattedMessage
-      defaultMessage="system.version_is_availble"
-      id="system.version_is_availble"
-      values={
-        Object {
-          "version": <b>
-            SonarQube 
-            5.6.7
-          </b>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="spacer-top"
-  >
-    Version 5.6.7 description
-  </p>
-  <div
-    className="big-spacer-top"
-  >
-    <DateFormatter
-      date="2017-03-01"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <a
-      className="spacer-left"
-      href="http://changelog.url/"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.release_notes
-    </a>
-  </div>
-  <SystemUpgradeIntermediate
-    className="spacer-top"
-    upgrades={
-      Array [
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.6 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-04-02",
-          "version": "5.6.6",
-        },
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.5 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-03-01",
-          "version": "5.6.5",
-        },
-      ]
-    }
-  />
-  <div
-    className="big-spacer-top"
-  >
-    <a
-      className="button"
-      download="http://download.url/community"
-      href="http://download.url/community"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.download_x.5.6.7
-    </a>
-    <a
-      className="spacer-left"
-      href="https://redirect.sonarsource.com/doc/upgrading.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.how_to_upgrade
-    </a>
-  </div>
-</div>
-`;
-
-exports[`should display correctly 2`] = `
-<div
-  className="system-upgrade-version"
->
-  <h3
-    className="h1 spacer-bottom"
-  >
-    <strong>
-      system.lts_version
-    </strong>
-  </h3>
-  <p>
-    <FormattedMessage
-      defaultMessage="system.version_is_availble"
-      id="system.version_is_availble"
-      values={
-        Object {
-          "version": <b>
-            SonarQube 
-            5.6.7
-          </b>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="spacer-top"
-  >
-    Version 5.6.7 description
-  </p>
-  <div
-    className="big-spacer-top"
-  >
-    <DateFormatter
-      date="2017-03-01"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <a
-      className="spacer-left"
-      href="http://changelog.url/"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.release_notes
-    </a>
-  </div>
-  <SystemUpgradeIntermediate
-    className="spacer-top"
-    upgrades={
-      Array [
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.6 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-04-02",
-          "version": "5.6.6",
-        },
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.5 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-03-01",
-          "version": "5.6.5",
-        },
-      ]
-    }
-  />
-  <div
-    className="big-spacer-top"
-  >
-    <a
-      className="button"
-      download="http://download.url/community"
-      href="http://download.url/community"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.download_x.5.6.7
-    </a>
-    <a
-      className="spacer-left"
-      href="https://redirect.sonarsource.com/doc/upgrading.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.how_to_upgrade
-    </a>
-  </div>
-</div>
-`;
-
-exports[`should display correctly 3`] = `
-<div
-  className="system-upgrade-version"
->
-  <h3
-    className="h1 spacer-bottom"
-  >
-    <strong>
-      system.latest_version
-    </strong>
-    <a
-      className="spacer-left medium"
-      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.see_whats_new
-    </a>
-  </h3>
-  <p>
-    <FormattedMessage
-      defaultMessage="system.version_is_availble"
-      id="system.version_is_availble"
-      values={
-        Object {
-          "version": <b>
-            SonarQube 
-            5.6.7
-          </b>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="spacer-top"
-  >
-    Version 5.6.7 description
-  </p>
-  <div
-    className="big-spacer-top"
-  >
-    <DateFormatter
-      date="2017-03-01"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <a
-      className="spacer-left"
-      href="http://changelog.url/"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.release_notes
-    </a>
-  </div>
-  <SystemUpgradeIntermediate
-    className="spacer-top"
-    upgrades={
-      Array [
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.6 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-04-02",
-          "version": "5.6.6",
-        },
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.5 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-03-01",
-          "version": "5.6.5",
-        },
-      ]
-    }
-  />
-  <div
-    className="big-spacer-top"
-  >
-    <a
-      className="button"
-      download="http://download.url/developer"
-      href="http://download.url/developer"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.download_x.5.6.7
-    </a>
-    <a
-      className="spacer-left"
-      href="https://redirect.sonarsource.com/doc/upgrading.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.how_to_upgrade
-    </a>
-  </div>
-</div>
-`;
-
-exports[`should display correctly 4`] = `
-<div
-  className="system-upgrade-version"
->
-  <h3
-    className="h1 spacer-bottom"
-  >
-    <strong>
-      system.latest_version
-    </strong>
-    <a
-      className="spacer-left medium"
-      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.see_whats_new
-    </a>
-  </h3>
-  <p>
-    <FormattedMessage
-      defaultMessage="system.version_is_availble"
-      id="system.version_is_availble"
-      values={
-        Object {
-          "version": <b>
-            SonarQube 
-            5.6.7
-          </b>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="spacer-top"
-  >
-    Version 5.6.7 description
-  </p>
-  <div
-    className="big-spacer-top"
-  >
-    <DateFormatter
-      date="2017-03-01"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <a
-      className="spacer-left"
-      href="http://changelog.url/"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.release_notes
-    </a>
-  </div>
-  <SystemUpgradeIntermediate
-    className="spacer-top"
-    upgrades={
-      Array [
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.6 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-04-02",
-          "version": "5.6.6",
-        },
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.5 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-03-01",
-          "version": "5.6.5",
-        },
-      ]
-    }
-  />
-  <div
-    className="big-spacer-top"
-  >
-    <a
-      className="button"
-      download="http://download.url/enterprise"
-      href="http://download.url/enterprise"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.download_x.5.6.7
-    </a>
-    <a
-      className="spacer-left"
-      href="https://redirect.sonarsource.com/doc/upgrading.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.how_to_upgrade
-    </a>
-  </div>
-</div>
-`;
-
-exports[`should display correctly 5`] = `
-<div
-  className="system-upgrade-version"
->
-  <h3
-    className="h1 spacer-bottom"
-  >
-    <strong>
-      system.latest_version
-    </strong>
-    <a
-      className="spacer-left medium"
-      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.see_whats_new
-    </a>
-  </h3>
-  <p>
-    <FormattedMessage
-      defaultMessage="system.version_is_availble"
-      id="system.version_is_availble"
-      values={
-        Object {
-          "version": <b>
-            SonarQube 
-            5.6.7
-          </b>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="spacer-top"
-  >
-    Version 5.6.7 description
-  </p>
-  <div
-    className="big-spacer-top"
-  >
-    <DateFormatter
-      date="2017-03-01"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <a
-      className="spacer-left"
-      href="http://changelog.url/"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.release_notes
-    </a>
-  </div>
-  <SystemUpgradeIntermediate
-    className="spacer-top"
-    upgrades={
-      Array [
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.6 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-04-02",
-          "version": "5.6.6",
-        },
-        Object {
-          "changeLogUrl": "http://changelog.url/",
-          "description": "Version 5.6.5 description",
-          "downloadDeveloperUrl": "http://download.url/developer",
-          "downloadUrl": "http://download.url/community",
-          "releaseDate": "2017-03-01",
-          "version": "5.6.5",
-        },
-      ]
-    }
-  />
-  <div
-    className="big-spacer-top"
-  >
-    <a
-      className="button"
-      download="http://download.url/datacenter"
-      href="http://download.url/datacenter"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.download_x.5.6.7
-    </a>
-    <a
-      className="spacer-left"
-      href="https://redirect.sonarsource.com/doc/upgrading.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.how_to_upgrade
-    </a>
-  </div>
-</div>
-`;
-
-exports[`should display correctly 6`] = `
-<div
-  className="system-upgrade-version"
->
-  <h3
-    className="h1 spacer-bottom"
-  >
-    <strong>
-      system.latest_version
-    </strong>
-    <a
-      className="spacer-left medium"
-      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.see_whats_new
-    </a>
-  </h3>
-  <p>
-    <FormattedMessage
-      defaultMessage="system.version_is_availble"
-      id="system.version_is_availble"
-      values={
-        Object {
-          "version": <b>
-            SonarQube 
-            5.6.7
-          </b>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="spacer-top"
-  >
-    Version 5.6.7 description
-  </p>
-  <div
-    className="big-spacer-top"
-  >
-    <DateFormatter
-      date="2017-03-01"
-      long={true}
-    >
-      <Component />
-    </DateFormatter>
-    <a
-      className="spacer-left"
-      href="http://changelog.url/"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.release_notes
-    </a>
-  </div>
-  <SystemUpgradeIntermediate
-    className="spacer-top"
-    upgrades={Array []}
-  />
-  <div
-    className="big-spacer-top"
-  >
-    <a
-      className="button"
-      download="http://download.url/community"
-      href="http://download.url/community"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.download_x.5.6.7
-    </a>
-    <a
-      className="spacer-left"
-      href="https://redirect.sonarsource.com/doc/upgrading.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      system.how_to_upgrade
-    </a>
-  </div>
-</div>
-`;
index 88e3fa2d032cca0f0f29c3dd705eff52c212a15d..3e3d05a2f7bd3d9e46ac73753432d7938fe0ef6e 100644 (file)
@@ -8,61 +8,32 @@ exports[`should render correctly 1`] = `
     variant="info"
   >
     system.new_version_available
-    <Button
-      className="spacer-left"
-      onClick={[Function]}
-    >
-      learn_more
-    </Button>
-  </Alert>
-</div>
-`;
-
-exports[`should render correctly 2`] = `
-<div
-  className="page-notifs"
->
-  <Alert
-    variant="info"
-  >
-    system.new_version_available
-    <Button
-      className="spacer-left"
-      onClick={[Function]}
-    >
-      learn_more
-    </Button>
-  </Alert>
-  <Connect(withAppState(SystemUpgradeForm))
-    onClose={[Function]}
-    systemUpgrades={
-      Array [
+    <SystemUpgradeButton
+      systemUpgrades={
         Array [
           Object {
             "changeLogUrl": "changelogurl",
-            "description": "Version 6.4 description",
+            "description": "Version 5.6.7 description",
             "downloadUrl": "downloadurl",
             "plugins": Object {},
-            "releaseDate": "2017-06-02",
-            "version": "6.4",
+            "releaseDate": "2017-03-01",
+            "version": "5.6.7",
           },
           Object {
             "changeLogUrl": "changelogurl",
-            "description": "Version 6.3 description",
+            "description": "Version 5.6.5 description",
             "downloadUrl": "downloadurl",
             "plugins": Object {},
-            "releaseDate": "2017-05-02",
-            "version": "6.3",
+            "releaseDate": "2017-03-01",
+            "version": "5.6.5",
           },
-        ],
-        Array [
           Object {
             "changeLogUrl": "changelogurl",
-            "description": "Version 5.6.7 description",
+            "description": "Version 6.3 description",
             "downloadUrl": "downloadurl",
             "plugins": Object {},
-            "releaseDate": "2017-03-01",
-            "version": "5.6.7",
+            "releaseDate": "2017-05-02",
+            "version": "6.3",
           },
           Object {
             "changeLogUrl": "changelogurl",
@@ -74,15 +45,15 @@ exports[`should render correctly 2`] = `
           },
           Object {
             "changeLogUrl": "changelogurl",
-            "description": "Version 5.6.5 description",
+            "description": "Version 6.4 description",
             "downloadUrl": "downloadurl",
             "plugins": Object {},
-            "releaseDate": "2017-03-01",
-            "version": "5.6.5",
+            "releaseDate": "2017-06-02",
+            "version": "6.4",
           },
-        ],
-      ]
-    }
-  />
+        ]
+      }
+    />
+  </Alert>
 </div>
 `;
index 4a4c7f282420d91f0f753181dfe34d0c92bf9d4d..a9857503449f1b503cc3d39a8d01994b67144584 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { each, groupBy, memoize, omit, omitBy, pickBy, sortBy } from 'lodash';
+import { each, memoize, omit, omitBy, pickBy, sortBy } from 'lodash';
 import { formatMeasure } from '../../helpers/measures';
 import { cleanQuery, parseAsArray, parseAsString, serializeStringArray } from '../../helpers/query';
-import { SystemUpgrade } from '../../types/system';
 
 export interface Query {
   expandedCards: string[];
@@ -229,17 +228,3 @@ export const serializeQuery = memoize(
       expand: serializeStringArray(query.expandedCards)
     })
 );
-
-export function sortUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[] {
-  return sortBy(upgrades, [
-    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[0]),
-    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[1] || 0),
-    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[2] || 0)
-  ]);
-}
-
-export function groupUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[][] {
-  const groupedVersions = groupBy(upgrades, upgrade => upgrade.version.split('.')[0]);
-  const sortedMajor = sortBy(Object.keys(groupedVersions), key => -Number(key));
-  return sortedMajor.map(key => groupedVersions[key]);
-}
index 0c7f2435632f65e3167b667e0c57ee49342b97d3..5ed07a1d665dc675dcdbb2f6024d97b880e6d0a6 100644 (file)
@@ -30,7 +30,7 @@ import InfoIcon from '../icons/InfoIcon';
 import DeferredSpinner from './DeferredSpinner';
 
 type AlertDisplay = 'banner' | 'inline' | 'block';
-type AlertVariant = 'error' | 'warning' | 'success' | 'info' | 'loading';
+export type AlertVariant = 'error' | 'warning' | 'success' | 'info' | 'loading';
 
 export interface AlertProps {
   display?: AlertDisplay;
index b6f5e08fb3339a67ce30d1571a3cd664a289194a..2d3ae9f0a30234dcb42111bd9b8406c0a203cc80 100644 (file)
@@ -45,25 +45,25 @@ export default function DismissableAlert(props: DismissableAlertProps) {
   }, [alertKey]);
 
   const hideAlert = () => {
+    window.dispatchEvent(new Event('resize'));
     save(DISMISSED_ALERT_STORAGE_KEY, 'true', alertKey);
   };
 
   return !show ? null : (
-    <Alert
-      className={classNames(`dismissable-alert-${display}`, className)}
-      display={display}
-      variant={variant}>
-      <div className="display-flex-center dismissable-alert-content">
-        <div className="flex-1">{children}</div>
-        <ButtonIcon
-          aria-label={translate('alert.dismiss')}
-          onClick={() => {
-            hideAlert();
-            setShow(false);
-          }}>
-          <ClearIcon size={12} thin={true} />
-        </ButtonIcon>
-      </div>
-    </Alert>
+    <div className={classNames('dismissable-alert-wrapper', className)}>
+      <Alert className={`dismissable-alert-${display}`} display={display} variant={variant}>
+        <div className="display-flex-center dismissable-alert-content">
+          <div className="flex-1">{children}</div>
+          <ButtonIcon
+            aria-label={translate('alert.dismiss')}
+            onClick={() => {
+              hideAlert();
+              setShow(false);
+            }}>
+            <ClearIcon size={12} thin={true} />
+          </ButtonIcon>
+        </div>
+      </Alert>
+    </div>
   );
 }
index 7583076acd6e05b4e003832dbcd9081b1cb09f5d..4c3e6bb89e76c2669b3d4d0e40b93c93e41edda7 100644 (file)
@@ -1,59 +1,67 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly 1`] = `
-<Alert
-  className="dismissable-alert-banner"
-  display="banner"
-  variant="info"
+<div
+  className="dismissable-alert-wrapper"
 >
-  <div
-    className="display-flex-center dismissable-alert-content"
+  <Alert
+    className="dismissable-alert-banner"
+    display="banner"
+    variant="info"
   >
     <div
-      className="flex-1"
+      className="display-flex-center dismissable-alert-content"
     >
-      <div>
-        My content
+      <div
+        className="flex-1"
+      >
+        <div>
+          My content
+        </div>
       </div>
+      <ButtonIcon
+        aria-label="alert.dismiss"
+        onClick={[Function]}
+      >
+        <ClearIcon
+          size={12}
+          thin={true}
+        />
+      </ButtonIcon>
     </div>
-    <ButtonIcon
-      aria-label="alert.dismiss"
-      onClick={[Function]}
-    >
-      <ClearIcon
-        size={12}
-        thin={true}
-      />
-    </ButtonIcon>
-  </div>
-</Alert>
+  </Alert>
+</div>
 `;
 
 exports[`should render correctly with a non-default display 1`] = `
-<Alert
-  className="dismissable-alert-block"
-  display="block"
-  variant="info"
+<div
+  className="dismissable-alert-wrapper"
 >
-  <div
-    className="display-flex-center dismissable-alert-content"
+  <Alert
+    className="dismissable-alert-block"
+    display="block"
+    variant="info"
   >
     <div
-      className="flex-1"
+      className="display-flex-center dismissable-alert-content"
     >
-      <div>
-        My content
+      <div
+        className="flex-1"
+      >
+        <div>
+          My content
+        </div>
       </div>
+      <ButtonIcon
+        aria-label="alert.dismiss"
+        onClick={[Function]}
+      >
+        <ClearIcon
+          size={12}
+          thin={true}
+        />
+      </ButtonIcon>
     </div>
-    <ButtonIcon
-      aria-label="alert.dismiss"
-      onClick={[Function]}
-    >
-      <ClearIcon
-        size={12}
-        thin={true}
-      />
-    </ButtonIcon>
-  </div>
-</Alert>
+  </Alert>
+</div>
 `;
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
new file mode 100644 (file)
index 0000000..cd3a527
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from '../../helpers/l10n';
+import { SystemUpgrade } from '../../types/system';
+import { Button } from '../controls/buttons';
+import SystemUpgradeForm from './SystemUpgradeForm';
+import { groupUpgrades, sortUpgrades } from './utils';
+
+interface Props {
+  systemUpgrades: SystemUpgrade[];
+}
+
+interface State {
+  openSystemUpgradeForm: boolean;
+}
+
+export default class SystemUpgradeButton extends React.PureComponent<Props, State> {
+  state: State = { openSystemUpgradeForm: false };
+
+  handleOpenSystemUpgradeForm = () => {
+    this.setState({ openSystemUpgradeForm: true });
+  };
+
+  handleCloseSystemUpgradeForm = () => {
+    this.setState({ openSystemUpgradeForm: false });
+  };
+
+  render() {
+    const { systemUpgrades } = this.props;
+    const { openSystemUpgradeForm } = this.state;
+    return (
+      <>
+        <Button className="spacer-left" onClick={this.handleOpenSystemUpgradeForm}>
+          {translate('learn_more')}
+        </Button>
+        {openSystemUpgradeForm && (
+          <SystemUpgradeForm
+            onClose={this.handleCloseSystemUpgradeForm}
+            systemUpgrades={groupUpgrades(sortUpgrades(systemUpgrades))}
+          />
+        )}
+      </>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
new file mode 100644 (file)
index 0000000..9a845db
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from '../../helpers/l10n';
+import { EditionKey } from '../../types/editions';
+import { SystemUpgrade } from '../../types/system';
+import { ResetButtonLink } from '../controls/buttons';
+import Modal from '../controls/Modal';
+import { withAppState } from '../hoc/withAppState';
+import SystemUpgradeItem from './SystemUpgradeItem';
+
+interface Props {
+  appState: Pick<T.AppState, 'edition'>;
+  onClose: () => void;
+  systemUpgrades: SystemUpgrade[][];
+}
+
+interface State {
+  upgrading: boolean;
+}
+
+export class SystemUpgradeForm extends React.PureComponent<Props, State> {
+  state: State = { upgrading: false };
+
+  render() {
+    const { upgrading } = this.state;
+    const { appState, systemUpgrades } = this.props;
+    const header = translate('system.system_upgrade');
+    return (
+      <Modal contentLabel={header} onRequestClose={this.props.onClose}>
+        <div className="modal-head">
+          <h2>{header}</h2>
+        </div>
+        <div className="modal-body">
+          {systemUpgrades.map((upgrades, idx) => (
+            <SystemUpgradeItem
+              edition={
+                appState.edition as EditionKey /* TODO: Fix once AppState is no longer ambiant. */
+              }
+              key={upgrades[upgrades.length - 1].version}
+              systemUpgrades={upgrades}
+              isLatestVersion={idx === 0}
+            />
+          ))}
+        </div>
+        <div className="modal-foot">
+          {upgrading && <i className="spinner spacer-right" />}
+          <a
+            className="pull-left"
+            href="https://www.sonarqube.org/downloads/?referrer=sonarqube"
+            rel="noopener noreferrer"
+            target="_blank">
+            {translate('system.see_sonarqube_downloads')}
+          </a>
+          <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
+        </div>
+      </Modal>
+    );
+  }
+}
+
+export default withAppState(SystemUpgradeForm);
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeIntermediate.tsx
new file mode 100644 (file)
index 0000000..db09a7e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from '../../helpers/l10n';
+import { SystemUpgrade } from '../../types/system';
+import { ButtonLink } from '../controls/buttons';
+import DropdownIcon from '../icons/DropdownIcon';
+import DateFormatter from '../intl/DateFormatter';
+
+interface Props {
+  className?: string;
+  upgrades: SystemUpgrade[];
+}
+
+interface State {
+  showMore: boolean;
+}
+
+export default class SystemUpgradeIntermediate extends React.PureComponent<Props, State> {
+  state: State = { showMore: false };
+
+  toggleIntermediatVersions = () => {
+    this.setState(state => ({ showMore: !state.showMore }));
+  };
+
+  render() {
+    const { showMore } = this.state;
+    const { upgrades } = this.props;
+    if (upgrades.length <= 0) {
+      return null;
+    }
+
+    return (
+      <div className={this.props.className}>
+        <ButtonLink className="little-spacer-bottom" onClick={this.toggleIntermediatVersions}>
+          {showMore
+            ? translate('system.hide_intermediate_versions')
+            : translate('system.show_intermediate_versions')}
+          <DropdownIcon className="little-spacer-left" turned={showMore} />
+        </ButtonLink>
+        {showMore &&
+          upgrades.map(upgrade => (
+            <div className="note system-upgrade-intermediate" key={upgrade.version}>
+              {upgrade.releaseDate && (
+                <DateFormatter date={upgrade.releaseDate} long={true}>
+                  {formattedDate => (
+                    <p>
+                      <b className="little-spacer-right">SonarQube {upgrade.version}</b>
+                      {formattedDate}
+                      {upgrade.changeLogUrl && (
+                        <a
+                          className="spacer-left"
+                          href={upgrade.changeLogUrl}
+                          rel="noopener noreferrer"
+                          target="_blank">
+                          {translate('system.release_notes')}
+                        </a>
+                      )}
+                    </p>
+                  )}
+                </DateFormatter>
+              )}
+              {upgrade.description && <p className="little-spacer-top">{upgrade.description}</p>}
+            </div>
+          ))}
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
new file mode 100644 (file)
index 0000000..7564554
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import {
+  getEdition,
+  getEditionDownloadFilename,
+  getEditionDownloadUrl
+} from '../../helpers/editions';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+import { EditionKey } from '../../types/editions';
+import { SystemUpgrade } from '../../types/system';
+import DateFormatter from '../intl/DateFormatter';
+import SystemUpgradeIntermediate from './SystemUpgradeIntermediate';
+
+export interface SystemUpgradeItemProps {
+  edition: EditionKey | undefined;
+  isLatestVersion: boolean;
+  systemUpgrades: SystemUpgrade[];
+}
+
+export default function SystemUpgradeItem(props: SystemUpgradeItemProps) {
+  const { edition, isLatestVersion, systemUpgrades } = props;
+  const lastUpgrade = systemUpgrades[0];
+  const downloadUrl = getEditionDownloadUrl(
+    getEdition(edition || EditionKey.community),
+    lastUpgrade
+  );
+
+  return (
+    <div className="system-upgrade-version">
+      <h3 className="h1 spacer-bottom">
+        <strong>
+          {isLatestVersion ? translate('system.latest_version') : translate('system.lts_version')}
+        </strong>
+        {isLatestVersion && (
+          <a
+            className="spacer-left medium"
+            href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
+            rel="noopener noreferrer"
+            target="_blank">
+            {translate('system.see_whats_new')}
+          </a>
+        )}
+      </h3>
+      <p>
+        <FormattedMessage
+          defaultMessage={translate('system.version_is_availble')}
+          id="system.version_is_availble"
+          values={{ version: <b>SonarQube {lastUpgrade.version}</b> }}
+        />
+      </p>
+      <p className="spacer-top">{lastUpgrade.description}</p>
+      <div className="big-spacer-top">
+        {lastUpgrade.releaseDate && (
+          <DateFormatter date={lastUpgrade.releaseDate} long={true}>
+            {formattedDate => (
+              <span>{translateWithParameters('system.released_x', formattedDate)}</span>
+            )}
+          </DateFormatter>
+        )}
+        {lastUpgrade.changeLogUrl && (
+          <a
+            className="spacer-left"
+            href={lastUpgrade.changeLogUrl}
+            rel="noopener noreferrer"
+            target="_blank">
+            {translate('system.release_notes')}
+          </a>
+        )}
+      </div>
+      <SystemUpgradeIntermediate className="spacer-top" upgrades={systemUpgrades.slice(1)} />
+      <div className="big-spacer-top">
+        <a
+          className="button"
+          download={getEditionDownloadFilename(downloadUrl)}
+          href={downloadUrl}
+          rel="noopener noreferrer"
+          target="_blank">
+          {translateWithParameters('system.download_x', lastUpgrade.version)}
+        </a>
+        <a
+          className="spacer-left"
+          href="https://redirect.sonarsource.com/doc/upgrading.html"
+          rel="noopener noreferrer"
+          target="_blank">
+          {translate('system.how_to_upgrade')}
+        </a>
+      </div>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeButton-test.tsx b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeButton-test.tsx
new file mode 100644 (file)
index 0000000..df853f7
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { click } from '../../../helpers/testUtils';
+import { Button } from '../../controls/buttons';
+import SystemUpgradeButton from '../SystemUpgradeButton';
+import SystemUpgradeForm from '../SystemUpgradeForm';
+
+it('should open modal correctly', () => {
+  const wrapper = shallowRender();
+  expect(wrapper).toMatchSnapshot();
+  click(wrapper.find(Button));
+  expect(wrapper.find(SystemUpgradeForm)).toBeDefined();
+});
+
+function shallowRender(props: Partial<SystemUpgradeButton['props']> = {}) {
+  return shallow<SystemUpgradeButton['props']>(
+    <SystemUpgradeButton systemUpgrades={[]} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx
new file mode 100644 (file)
index 0000000..7e33af4
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { EditionKey } from '../../../types/editions';
+import { SystemUpgradeForm } from '../SystemUpgradeForm';
+
+const UPGRADES = [
+  [
+    {
+      version: '6.4',
+      description: 'Version 6.4 description',
+      releaseDate: '2017-06-02',
+      changeLogUrl: 'changelogurl',
+      downloadUrl: 'downloadurl',
+      plugins: {}
+    },
+    {
+      version: '6.3',
+      description: 'Version 6.3 description',
+      releaseDate: '2017-05-02',
+      changeLogUrl: 'changelogurl',
+      downloadUrl: 'downloadurl',
+      plugins: {}
+    }
+  ],
+  [
+    {
+      version: '5.6.7',
+      description: 'Version 5.6.7 description',
+      releaseDate: '2017-03-01',
+      changeLogUrl: 'changelogurl',
+      downloadUrl: 'downloadurl',
+      plugins: {}
+    },
+    {
+      version: '5.6.6',
+      description: 'Version 5.6.6 description',
+      releaseDate: '2017-04-02',
+      changeLogUrl: 'changelogurl',
+      downloadUrl: 'downloadurl',
+      plugins: {}
+    },
+    {
+      version: '5.6.5',
+      description: 'Version 5.6.5 description',
+      releaseDate: '2017-03-01',
+      changeLogUrl: 'changelogurl',
+      downloadUrl: 'downloadurl',
+      plugins: {}
+    }
+  ]
+];
+
+it('should display correctly', () => {
+  expect(
+    shallow(
+      <SystemUpgradeForm
+        appState={{ edition: EditionKey.community }}
+        onClose={jest.fn()}
+        systemUpgrades={UPGRADES}
+      />
+    )
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeIntermediate-test.tsx b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeIntermediate-test.tsx
new file mode 100644 (file)
index 0000000..23d79ea
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { click } from '../../../helpers/testUtils';
+import SystemUpgradeIntermediate from '../SystemUpgradeIntermediate';
+
+const UPGRADES = [
+  {
+    version: '5.6.6',
+    description: 'Version 5.6.6 description',
+    releaseDate: '2017-04-02',
+    changeLogUrl: 'changelogurl',
+    downloadUrl: 'downloadurl',
+    plugins: {}
+  },
+  {
+    version: '5.6.5',
+    description: 'Version 5.6.5 description',
+    releaseDate: '2017-03-01',
+    changeLogUrl: 'changelogurl',
+    downloadUrl: 'downloadurl',
+    plugins: {}
+  }
+];
+
+it('should display correctly', () => {
+  const wrapper = shallow(<SystemUpgradeIntermediate upgrades={UPGRADES} />);
+  expect(wrapper).toMatchSnapshot();
+  wrapper.setState({ showMore: true });
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('should allow to show and hide intermediates', () => {
+  const wrapper = shallow(<SystemUpgradeIntermediate upgrades={UPGRADES} />);
+  expect(wrapper.find('.system-upgrade-intermediate').exists()).toBe(false);
+  click(wrapper.find('ButtonLink'));
+  expect(wrapper.find('.system-upgrade-intermediate').exists()).toBe(true);
+  click(wrapper.find('ButtonLink'));
+  expect(wrapper.find('.system-upgrade-intermediate').exists()).toBe(false);
+});
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeItem-test.tsx b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeItem-test.tsx
new file mode 100644 (file)
index 0000000..ba705ab
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { EditionKey } from '../../../types/editions';
+import SystemUpgradeItem, { SystemUpgradeItemProps } from '../SystemUpgradeItem';
+
+it('should display correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ isLatestVersion: false })).toMatchSnapshot();
+  expect(shallowRender({ edition: EditionKey.developer })).toMatchSnapshot();
+  expect(shallowRender({ edition: EditionKey.enterprise })).toMatchSnapshot();
+  expect(shallowRender({ edition: EditionKey.datacenter })).toMatchSnapshot();
+  // Fallback to Community.
+  expect(
+    shallowRender({
+      systemUpgrades: [
+        {
+          version: '5.6.7',
+          description: 'Version 5.6.7 description',
+          releaseDate: '2017-03-01',
+          changeLogUrl: 'http://changelog.url/',
+          downloadUrl: 'http://download.url/community'
+        }
+      ]
+    })
+  ).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<SystemUpgradeItemProps> = {}) {
+  return shallow<SystemUpgradeItemProps>(
+    <SystemUpgradeItem
+      edition={EditionKey.community}
+      systemUpgrades={[
+        {
+          version: '5.6.7',
+          description: 'Version 5.6.7 description',
+          releaseDate: '2017-03-01',
+          changeLogUrl: 'http://changelog.url/',
+          downloadUrl: 'http://download.url/community',
+          downloadDeveloperUrl: 'http://download.url/developer',
+          downloadEnterpriseUrl: 'http://download.url/enterprise',
+          downloadDatacenterUrl: 'http://download.url/datacenter'
+        },
+        {
+          version: '5.6.6',
+          description: 'Version 5.6.6 description',
+          releaseDate: '2017-04-02',
+          changeLogUrl: 'http://changelog.url/',
+          downloadUrl: 'http://download.url/community',
+          downloadDeveloperUrl: 'http://download.url/developer'
+        },
+        {
+          version: '5.6.5',
+          description: 'Version 5.6.5 description',
+          releaseDate: '2017-03-01',
+          changeLogUrl: 'http://changelog.url/',
+          downloadUrl: 'http://download.url/community',
+          downloadDeveloperUrl: 'http://download.url/developer'
+        }
+      ]}
+      isLatestVersion={true}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeButton-test.tsx.snap b/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeButton-test.tsx.snap
new file mode 100644 (file)
index 0000000..037b14b
--- /dev/null
@@ -0,0 +1,12 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should open modal correctly 1`] = `
+<Fragment>
+  <Button
+    className="spacer-left"
+    onClick={[Function]}
+  >
+    learn_more
+  </Button>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeForm-test.tsx.snap b/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeForm-test.tsx.snap
new file mode 100644 (file)
index 0000000..e1c8041
--- /dev/null
@@ -0,0 +1,95 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display correctly 1`] = `
+<Modal
+  contentLabel="system.system_upgrade"
+  onRequestClose={[MockFunction]}
+>
+  <div
+    className="modal-head"
+  >
+    <h2>
+      system.system_upgrade
+    </h2>
+  </div>
+  <div
+    className="modal-body"
+  >
+    <SystemUpgradeItem
+      edition="community"
+      isLatestVersion={true}
+      key="6.3"
+      systemUpgrades={
+        Array [
+          Object {
+            "changeLogUrl": "changelogurl",
+            "description": "Version 6.4 description",
+            "downloadUrl": "downloadurl",
+            "plugins": Object {},
+            "releaseDate": "2017-06-02",
+            "version": "6.4",
+          },
+          Object {
+            "changeLogUrl": "changelogurl",
+            "description": "Version 6.3 description",
+            "downloadUrl": "downloadurl",
+            "plugins": Object {},
+            "releaseDate": "2017-05-02",
+            "version": "6.3",
+          },
+        ]
+      }
+    />
+    <SystemUpgradeItem
+      edition="community"
+      isLatestVersion={false}
+      key="5.6.5"
+      systemUpgrades={
+        Array [
+          Object {
+            "changeLogUrl": "changelogurl",
+            "description": "Version 5.6.7 description",
+            "downloadUrl": "downloadurl",
+            "plugins": Object {},
+            "releaseDate": "2017-03-01",
+            "version": "5.6.7",
+          },
+          Object {
+            "changeLogUrl": "changelogurl",
+            "description": "Version 5.6.6 description",
+            "downloadUrl": "downloadurl",
+            "plugins": Object {},
+            "releaseDate": "2017-04-02",
+            "version": "5.6.6",
+          },
+          Object {
+            "changeLogUrl": "changelogurl",
+            "description": "Version 5.6.5 description",
+            "downloadUrl": "downloadurl",
+            "plugins": Object {},
+            "releaseDate": "2017-03-01",
+            "version": "5.6.5",
+          },
+        ]
+      }
+    />
+  </div>
+  <div
+    className="modal-foot"
+  >
+    <a
+      className="pull-left"
+      href="https://www.sonarqube.org/downloads/?referrer=sonarqube"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.see_sonarqube_downloads
+    </a>
+    <ResetButtonLink
+      onClick={[MockFunction]}
+    >
+      cancel
+    </ResetButtonLink>
+  </div>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeIntermediate-test.tsx.snap b/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeIntermediate-test.tsx.snap
new file mode 100644 (file)
index 0000000..763ab35
--- /dev/null
@@ -0,0 +1,63 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display correctly 1`] = `
+<div>
+  <ButtonLink
+    className="little-spacer-bottom"
+    onClick={[Function]}
+  >
+    system.show_intermediate_versions
+    <DropdownIcon
+      className="little-spacer-left"
+      turned={false}
+    />
+  </ButtonLink>
+</div>
+`;
+
+exports[`should display correctly 2`] = `
+<div>
+  <ButtonLink
+    className="little-spacer-bottom"
+    onClick={[Function]}
+  >
+    system.hide_intermediate_versions
+    <DropdownIcon
+      className="little-spacer-left"
+      turned={true}
+    />
+  </ButtonLink>
+  <div
+    className="note system-upgrade-intermediate"
+    key="5.6.6"
+  >
+    <DateFormatter
+      date="2017-04-02"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <p
+      className="little-spacer-top"
+    >
+      Version 5.6.6 description
+    </p>
+  </div>
+  <div
+    className="note system-upgrade-intermediate"
+    key="5.6.5"
+  >
+    <DateFormatter
+      date="2017-03-01"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <p
+      className="little-spacer-top"
+    >
+      Version 5.6.5 description
+    </p>
+  </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeItem-test.tsx.snap b/server/sonar-web/src/main/js/components/upgrade/__tests__/__snapshots__/SystemUpgradeItem-test.tsx.snap
new file mode 100644 (file)
index 0000000..51d35d9
--- /dev/null
@@ -0,0 +1,592 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display correctly 1`] = `
+<div
+  className="system-upgrade-version"
+>
+  <h3
+    className="h1 spacer-bottom"
+  >
+    <strong>
+      system.latest_version
+    </strong>
+    <a
+      className="spacer-left medium"
+      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.see_whats_new
+    </a>
+  </h3>
+  <p>
+    <FormattedMessage
+      defaultMessage="system.version_is_availble"
+      id="system.version_is_availble"
+      values={
+        Object {
+          "version": <b>
+            SonarQube 
+            5.6.7
+          </b>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="spacer-top"
+  >
+    Version 5.6.7 description
+  </p>
+  <div
+    className="big-spacer-top"
+  >
+    <DateFormatter
+      date="2017-03-01"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <a
+      className="spacer-left"
+      href="http://changelog.url/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.release_notes
+    </a>
+  </div>
+  <SystemUpgradeIntermediate
+    className="spacer-top"
+    upgrades={
+      Array [
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.6 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-04-02",
+          "version": "5.6.6",
+        },
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.5 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-03-01",
+          "version": "5.6.5",
+        },
+      ]
+    }
+  />
+  <div
+    className="big-spacer-top"
+  >
+    <a
+      className="button"
+      download="http://download.url/community"
+      href="http://download.url/community"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.download_x.5.6.7
+    </a>
+    <a
+      className="spacer-left"
+      href="https://redirect.sonarsource.com/doc/upgrading.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.how_to_upgrade
+    </a>
+  </div>
+</div>
+`;
+
+exports[`should display correctly 2`] = `
+<div
+  className="system-upgrade-version"
+>
+  <h3
+    className="h1 spacer-bottom"
+  >
+    <strong>
+      system.lts_version
+    </strong>
+  </h3>
+  <p>
+    <FormattedMessage
+      defaultMessage="system.version_is_availble"
+      id="system.version_is_availble"
+      values={
+        Object {
+          "version": <b>
+            SonarQube 
+            5.6.7
+          </b>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="spacer-top"
+  >
+    Version 5.6.7 description
+  </p>
+  <div
+    className="big-spacer-top"
+  >
+    <DateFormatter
+      date="2017-03-01"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <a
+      className="spacer-left"
+      href="http://changelog.url/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.release_notes
+    </a>
+  </div>
+  <SystemUpgradeIntermediate
+    className="spacer-top"
+    upgrades={
+      Array [
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.6 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-04-02",
+          "version": "5.6.6",
+        },
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.5 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-03-01",
+          "version": "5.6.5",
+        },
+      ]
+    }
+  />
+  <div
+    className="big-spacer-top"
+  >
+    <a
+      className="button"
+      download="http://download.url/community"
+      href="http://download.url/community"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.download_x.5.6.7
+    </a>
+    <a
+      className="spacer-left"
+      href="https://redirect.sonarsource.com/doc/upgrading.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.how_to_upgrade
+    </a>
+  </div>
+</div>
+`;
+
+exports[`should display correctly 3`] = `
+<div
+  className="system-upgrade-version"
+>
+  <h3
+    className="h1 spacer-bottom"
+  >
+    <strong>
+      system.latest_version
+    </strong>
+    <a
+      className="spacer-left medium"
+      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.see_whats_new
+    </a>
+  </h3>
+  <p>
+    <FormattedMessage
+      defaultMessage="system.version_is_availble"
+      id="system.version_is_availble"
+      values={
+        Object {
+          "version": <b>
+            SonarQube 
+            5.6.7
+          </b>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="spacer-top"
+  >
+    Version 5.6.7 description
+  </p>
+  <div
+    className="big-spacer-top"
+  >
+    <DateFormatter
+      date="2017-03-01"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <a
+      className="spacer-left"
+      href="http://changelog.url/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.release_notes
+    </a>
+  </div>
+  <SystemUpgradeIntermediate
+    className="spacer-top"
+    upgrades={
+      Array [
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.6 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-04-02",
+          "version": "5.6.6",
+        },
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.5 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-03-01",
+          "version": "5.6.5",
+        },
+      ]
+    }
+  />
+  <div
+    className="big-spacer-top"
+  >
+    <a
+      className="button"
+      download="http://download.url/developer"
+      href="http://download.url/developer"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.download_x.5.6.7
+    </a>
+    <a
+      className="spacer-left"
+      href="https://redirect.sonarsource.com/doc/upgrading.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.how_to_upgrade
+    </a>
+  </div>
+</div>
+`;
+
+exports[`should display correctly 4`] = `
+<div
+  className="system-upgrade-version"
+>
+  <h3
+    className="h1 spacer-bottom"
+  >
+    <strong>
+      system.latest_version
+    </strong>
+    <a
+      className="spacer-left medium"
+      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.see_whats_new
+    </a>
+  </h3>
+  <p>
+    <FormattedMessage
+      defaultMessage="system.version_is_availble"
+      id="system.version_is_availble"
+      values={
+        Object {
+          "version": <b>
+            SonarQube 
+            5.6.7
+          </b>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="spacer-top"
+  >
+    Version 5.6.7 description
+  </p>
+  <div
+    className="big-spacer-top"
+  >
+    <DateFormatter
+      date="2017-03-01"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <a
+      className="spacer-left"
+      href="http://changelog.url/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.release_notes
+    </a>
+  </div>
+  <SystemUpgradeIntermediate
+    className="spacer-top"
+    upgrades={
+      Array [
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.6 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-04-02",
+          "version": "5.6.6",
+        },
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.5 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-03-01",
+          "version": "5.6.5",
+        },
+      ]
+    }
+  />
+  <div
+    className="big-spacer-top"
+  >
+    <a
+      className="button"
+      download="http://download.url/enterprise"
+      href="http://download.url/enterprise"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.download_x.5.6.7
+    </a>
+    <a
+      className="spacer-left"
+      href="https://redirect.sonarsource.com/doc/upgrading.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.how_to_upgrade
+    </a>
+  </div>
+</div>
+`;
+
+exports[`should display correctly 5`] = `
+<div
+  className="system-upgrade-version"
+>
+  <h3
+    className="h1 spacer-bottom"
+  >
+    <strong>
+      system.latest_version
+    </strong>
+    <a
+      className="spacer-left medium"
+      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.see_whats_new
+    </a>
+  </h3>
+  <p>
+    <FormattedMessage
+      defaultMessage="system.version_is_availble"
+      id="system.version_is_availble"
+      values={
+        Object {
+          "version": <b>
+            SonarQube 
+            5.6.7
+          </b>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="spacer-top"
+  >
+    Version 5.6.7 description
+  </p>
+  <div
+    className="big-spacer-top"
+  >
+    <DateFormatter
+      date="2017-03-01"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <a
+      className="spacer-left"
+      href="http://changelog.url/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.release_notes
+    </a>
+  </div>
+  <SystemUpgradeIntermediate
+    className="spacer-top"
+    upgrades={
+      Array [
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.6 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-04-02",
+          "version": "5.6.6",
+        },
+        Object {
+          "changeLogUrl": "http://changelog.url/",
+          "description": "Version 5.6.5 description",
+          "downloadDeveloperUrl": "http://download.url/developer",
+          "downloadUrl": "http://download.url/community",
+          "releaseDate": "2017-03-01",
+          "version": "5.6.5",
+        },
+      ]
+    }
+  />
+  <div
+    className="big-spacer-top"
+  >
+    <a
+      className="button"
+      download="http://download.url/datacenter"
+      href="http://download.url/datacenter"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.download_x.5.6.7
+    </a>
+    <a
+      className="spacer-left"
+      href="https://redirect.sonarsource.com/doc/upgrading.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.how_to_upgrade
+    </a>
+  </div>
+</div>
+`;
+
+exports[`should display correctly 6`] = `
+<div
+  className="system-upgrade-version"
+>
+  <h3
+    className="h1 spacer-bottom"
+  >
+    <strong>
+      system.latest_version
+    </strong>
+    <a
+      className="spacer-left medium"
+      href="https://www.sonarqube.org/whats-new/?referrer=sonarqube"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.see_whats_new
+    </a>
+  </h3>
+  <p>
+    <FormattedMessage
+      defaultMessage="system.version_is_availble"
+      id="system.version_is_availble"
+      values={
+        Object {
+          "version": <b>
+            SonarQube 
+            5.6.7
+          </b>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="spacer-top"
+  >
+    Version 5.6.7 description
+  </p>
+  <div
+    className="big-spacer-top"
+  >
+    <DateFormatter
+      date="2017-03-01"
+      long={true}
+    >
+      <Component />
+    </DateFormatter>
+    <a
+      className="spacer-left"
+      href="http://changelog.url/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.release_notes
+    </a>
+  </div>
+  <SystemUpgradeIntermediate
+    className="spacer-top"
+    upgrades={Array []}
+  />
+  <div
+    className="big-spacer-top"
+  >
+    <a
+      className="button"
+      download="http://download.url/community"
+      href="http://download.url/community"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.download_x.5.6.7
+    </a>
+    <a
+      className="spacer-left"
+      href="https://redirect.sonarsource.com/doc/upgrading.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      system.how_to_upgrade
+    </a>
+  </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/utils-test.ts b/server/sonar-web/src/main/js/components/upgrade/__tests__/utils-test.ts
new file mode 100644 (file)
index 0000000..34d1910
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 { SystemUpgrade } from '../../../types/system';
+import * as u from '../utils';
+
+describe('sortUpgrades', () => {
+  it('should sort correctly versions', () => {
+    expect(
+      u.sortUpgrades([
+        { version: '5.4.2' },
+        { version: '5.10' },
+        { version: '5.1' },
+        { version: '5.4' }
+      ] as SystemUpgrade[])
+    ).toEqual([{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]);
+    expect(
+      u.sortUpgrades([
+        { version: '5.10' },
+        { version: '5.1.2' },
+        { version: '6.0' },
+        { version: '6.9' }
+      ] as SystemUpgrade[])
+    ).toEqual([{ version: '6.9' }, { version: '6.0' }, { version: '5.10' }, { version: '5.1.2' }]);
+  });
+});
+
+describe('groupUpgrades', () => {
+  it('should group correctly', () => {
+    expect(
+      u.groupUpgrades([
+        { version: '5.10' },
+        { version: '5.4.2' },
+        { version: '5.4' },
+        { version: '5.1' }
+      ] as SystemUpgrade[])
+    ).toEqual([
+      [{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]
+    ]);
+    expect(
+      u.groupUpgrades([
+        { version: '6.9' },
+        { version: '6.7' },
+        { version: '6.0' },
+        { version: '5.10' },
+        { version: '5.4.2' }
+      ] as SystemUpgrade[])
+    ).toEqual([
+      [{ version: '6.9' }, { version: '6.7' }, { version: '6.0' }],
+      [{ version: '5.10' }, { version: '5.4.2' }]
+    ]);
+  });
+});
diff --git a/server/sonar-web/src/main/js/components/upgrade/utils.ts b/server/sonar-web/src/main/js/components/upgrade/utils.ts
new file mode 100644 (file)
index 0000000..e58b76a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 { groupBy, sortBy } from 'lodash';
+import { SystemUpgrade } from '../../types/system';
+
+export function sortUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[] {
+  return sortBy(upgrades, [
+    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[0]),
+    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[1] || 0),
+    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[2] || 0)
+  ]);
+}
+
+export function groupUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[][] {
+  const groupedVersions = groupBy(upgrades, upgrade => upgrade.version.split('.')[0]);
+  const sortedMajor = sortBy(Object.keys(groupedVersions), key => -Number(key));
+  return sortedMajor.map(key => groupedVersions[key]);
+}
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
new file mode 100644 (file)
index 0000000..ba1249c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 { SystemUpgrade } from '../../types/system';
+
+export function mockUpgrades(override: Partial<SystemUpgrade>): SystemUpgrade {
+  return {
+    version: '5.6.7',
+    description: 'Version 5.6.7 description',
+    releaseDate: '2017-03-01',
+    changeLogUrl: 'changelogurl',
+    downloadUrl: 'downloadurl',
+    ...override
+  };
+}
index 79260f4b39ae64310920f0bd8824ca87f8818731..6d422fa42e7f53e8edafd9795f8c42601d25f53a 100644 (file)
@@ -451,6 +451,17 @@ qualifier.description.VW=Potentially multi-level, management-oriented overview a
 qualifier.description.SVW=Potentially multi-level, management-oriented overview aggregation.
 qualifier.description.APP=Single-level aggregation with a technical focus and a project-like homepage.
 
+
+#------------------------------------------------------------------------------
+#
+# Admin notification 
+#
+#------------------------------------------------------------------------------
+admin_notification.update.new_minor_version=There’s a new version of SonarQube available. Update to enjoy the latest updates and features.
+admin_notification.update.new_patch=There’s an update available for your SonarQube instance. Please update to make sure you benefit from the latest security and bug fixes.
+admin_notification.update.pre_lts=You’re running a version of SonarQube that has reached end of life. Please upgrade to a supported version at your earliest convenience.
+admin_notification.update.previous_lts=You’re running a version of SonarQube that is past end of life. Please upgrade to a supported version immediately.
+
 #------------------------------------------------------------------------------
 #
 # PROJECT LINKS