diff options
author | Viktor Vorona <viktor.vorona@sonarsource.com> | 2023-07-05 14:57:30 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-07-07 20:03:21 +0000 |
commit | 6c0159dda108f42c4a68949443b566954eff2798 (patch) | |
tree | 287de88e9c29beeea6a687cd11c23137e35494ce | |
parent | 08cea0be114b6b08931bb713cff4b3738d52183d (diff) | |
download | sonarqube-6c0159dda108f42c4a68949443b566954eff2798.tar.gz sonarqube-6c0159dda108f42c4a68949443b566954eff2798.zip |
SONAR-18427 RTL Migration Marketplace
44 files changed, 649 insertions, 2781 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/PluginsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/PluginsServiceMock.ts new file mode 100644 index 00000000000..c5661f16d9e --- /dev/null +++ b/server/sonar-web/src/main/js/api/mocks/PluginsServiceMock.ts @@ -0,0 +1,241 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { cloneDeep, omit } from 'lodash'; +import { + mockAvailablePlugin, + mockInstalledPlugin, + mockRelease, + mockUpdate, +} from '../../helpers/mocks/plugins'; +import { AvailablePlugin, InstalledPlugin, PendingPluginResult } from '../../types/plugins'; +import { + cancelPendingPlugins, + getAvailablePlugins, + getInstalledPlugins, + getPendingPlugins, + getUpdatesPlugins, + installPlugin, + uninstallPlugin, + updatePlugin, +} from '../plugins'; + +jest.mock('../plugins'); + +const defaultAvailable: AvailablePlugin[] = [ + mockAvailablePlugin({ + key: 'foo', + name: 'CFoo', + category: 'Languages', + description: 'Description', + homepageUrl: 'https://www.sonarsource.com/', + issueTrackerUrl: 'https://www.sonarsource.com/', + organizationName: 'SonarSource', + organizationUrl: 'https://www.sonarsource.com/', + release: mockRelease({ + version: '1.0.0', + description: 'release description', + date: '2020-01-01', + changeLogUrl: 'https://www.sonarsource.com/', + }), + }), + mockAvailablePlugin({ + key: 'bar', + name: 'DTest', + editionBundled: true, + release: mockRelease({ + version: '2.0.0', + }), + }), + mockAvailablePlugin({ + description: 'Mocked Plugin', + termsAndConditionsUrl: 'https://www.sonarsource.com/', + }), +]; +const defaultInstalled: InstalledPlugin[] = [ + mockInstalledPlugin({ + key: 'test', + name: 'ATest_install', + version: '1.1.0', + }), + mockInstalledPlugin({ + key: 'one-update', + name: 'ZTest', + version: '1.1.1', + updates: [ + mockUpdate({ + status: 'COMPATIBLE', + release: mockRelease({ + version: '1.2.0', + date: '2020-01-01', + changeLogUrl: 'https://www.sonarsource.com/', + }), + }), + ], + }), + mockInstalledPlugin({ + key: 'multiple_updates', + name: 'BTest_update', + version: '1.2.0', + editionBundled: true, + updates: [ + mockUpdate({ + status: 'COMPATIBLE', + release: mockRelease({ version: '1.2.1', changeLogUrl: 'https://www.sonarsource.com/' }), + }), + mockUpdate({ + status: 'COMPATIBLE', + release: mockRelease({ version: '1.3.0', changeLogUrl: 'https://www.sonarsource.com/' }), + }), + mockUpdate({ + status: 'NON-COMPATIBLE', + release: mockRelease({ version: '1.4.0', changeLogUrl: 'https://www.sonarsource.com/' }), + }), + ], + }), +]; +const defaultPending: PendingPluginResult = { + installing: [], + removing: [], + updating: [], +}; + +export default class PluginsServiceMock { + #available: AvailablePlugin[]; + #installed: InstalledPlugin[]; + #pending: PendingPluginResult; + + constructor() { + this.#available = cloneDeep(defaultAvailable); + this.#installed = cloneDeep(defaultInstalled); + this.#pending = cloneDeep(defaultPending); + + jest.mocked(getAvailablePlugins).mockImplementation(this.handleGetAvailablePlugins); + jest.mocked(getPendingPlugins).mockImplementation(this.handleGetPendingPlugins); + jest.mocked(getInstalledPlugins).mockImplementation(this.handleGetInstalledPlugins); + jest.mocked(getUpdatesPlugins).mockImplementation(this.handleGetUpdatesPlugins); + jest.mocked(installPlugin).mockImplementation(this.handleInstallPlugin); + jest.mocked(uninstallPlugin).mockImplementation(this.handleUninstallPlugin); + jest.mocked(updatePlugin).mockImplementation(this.handleUpdatePlugin); + jest.mocked(cancelPendingPlugins).mockImplementation(this.handleCancelPendingPlugins); + } + + handleGetAvailablePlugins = () => { + return this.reply({ + plugins: this.#available, + updateCenterRefresh: '2021-01-01T00:00:00+0000', + }); + }; + + handleGetPendingPlugins = () => { + return this.reply(this.#pending); + }; + + handleGetInstalledPlugins = () => { + return this.reply(this.#installed.map((plugin) => omit(plugin, 'updates'))); + }; + + handleGetUpdatesPlugins = () => { + return this.reply({ + plugins: this.#installed + .filter((plugin) => plugin.updates && plugin.updates.length > 0) + .map((plugin) => omit(plugin, 'version', 'updatedAt')), + updateCenterRefresh: '2021-01-01T00:00:00+0000', + }); + }; + + handleGetPluginUpdates = () => { + return this.reply( + this.#installed.filter((plugin) => plugin.updates && plugin.updates.length > 0) + ); + }; + + handleInstallPlugin: typeof installPlugin = (data) => { + const plugin = this.#available.find((plugin) => plugin.key === data.key); + if (plugin === undefined) { + return Promise.reject(new Error('Plugin not found')); + } + this.#pending.installing.push({ + ...omit(plugin, 'release', 'update'), + version: plugin.release.version, + implementationBuild: '20210101-000000', + }); + return this.reply(); + }; + + handleUninstallPlugin: typeof uninstallPlugin = (data) => { + const plugin = this.#installed.find((plugin) => plugin.key === data.key); + if (plugin === undefined) { + return Promise.reject(new Error('Plugin not found')); + } + this.#pending.removing.push({ + ...omit( + plugin, + 'updates', + 'updatedAt', + 'sonarLintSupported', + 'hash', + 'filename', + 'documentationPath' + ), + implementationBuild: '20210101-000000', + }); + return this.reply(); + }; + + handleUpdatePlugin: typeof updatePlugin = (data) => { + const plugin = this.#installed + .filter((plugin) => plugin.updates && plugin.updates.length > 0) + .find((plugin) => plugin.key === data.key); + if (plugin === undefined || plugin.updates === undefined || plugin.updates.length === 0) { + return Promise.reject(new Error('Plugin not found')); + } + this.#pending.updating.push({ + ...omit( + plugin, + 'updates', + 'updatedAt', + 'sonarLintSupported', + 'hash', + 'filename', + 'documentationPath' + ), + version: plugin.updates[plugin.updates.length - 1].release?.version ?? '', + implementationBuild: '20210101-000000', + }); + return this.reply(); + }; + + handleCancelPendingPlugins: typeof cancelPendingPlugins = () => { + this.#pending = cloneDeep(defaultPending); + return this.reply(); + }; + + reset = () => { + this.#available = cloneDeep(defaultAvailable); + this.#installed = cloneDeep(defaultInstalled); + this.#pending = cloneDeep(defaultPending); + }; + + reply<T>(): Promise<void>; + reply<T>(response: T): Promise<T>; + reply<T>(response?: T): Promise<T | void> { + return Promise.resolve(response ? cloneDeep(response) : undefined); + } +} diff --git a/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts index aefca24cfd0..41ff5481689 100644 --- a/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/SettingsServiceMock.ts @@ -38,6 +38,7 @@ import { getValues, resetSettingValue, setSettingValue, + setSimpleSettingValue, } from '../settings'; jest.mock('../settings'); @@ -126,6 +127,7 @@ export default class SettingsServiceMock { jest.mocked(getValues).mockImplementation(this.handleGetValues); jest.mocked(getAllValues).mockImplementation(this.handleGetAllValues); jest.mocked(setSettingValue).mockImplementation(this.handleSetSettingValue); + jest.mocked(setSimpleSettingValue).mockImplementation(this.handleSetSimpleSettingValue); jest.mocked(resetSettingValue).mockImplementation(this.handleResetSettingValue); jest.mocked(checkSecretKey).mockImplementation(this.handleCheckSecretKey); jest.mocked(generateSecretKey).mockImplementation(this.handleGenerateSecretKey); @@ -219,6 +221,11 @@ export default class SettingsServiceMock { return this.reply({ encryptedValue: 'encryptedValue' }); }; + handleSetSimpleSettingValue: typeof setSimpleSettingValue = (data) => { + this.set(data.key, data.value); + return this.reply(undefined); + }; + setSecretKeyAvailable = (val = false) => { this.#secretKeyAvailable = val; }; diff --git a/server/sonar-web/src/main/js/api/plugins.ts b/server/sonar-web/src/main/js/api/plugins.ts index c970c2c3d56..8321da4fb6b 100644 --- a/server/sonar-web/src/main/js/api/plugins.ts +++ b/server/sonar-web/src/main/js/api/plugins.ts @@ -17,16 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { findLastIndex } from 'lodash'; import { throwGlobalError } from '../helpers/error'; import { getJSON, post } from '../helpers/request'; -import { isDefined } from '../helpers/types'; import { AvailablePlugin, InstalledPlugin, PendingPluginResult, PluginType, - Update, } from '../types/plugins'; export function getAvailablePlugins(): Promise<{ @@ -40,85 +37,17 @@ export function getPendingPlugins(): Promise<PendingPluginResult> { return getJSON('/api/plugins/pending').catch(throwGlobalError); } -function getLastUpdates(updates: undefined | Update[]): Update[] { - if (!updates) { - return []; - } - const lastUpdate = ['COMPATIBLE', 'REQUIRES_SYSTEM_UPGRADE', 'DEPS_REQUIRE_SYSTEM_UPGRADE'].map( - (status) => { - const index = findLastIndex(updates, (update) => update.status === status); - return index > -1 ? updates[index] : undefined; - } +export function getInstalledPlugins(type = PluginType.External): Promise<InstalledPlugin[]> { + return getJSON('/api/plugins/installed', { f: 'category', type }).then( + ({ plugins }) => plugins, + throwGlobalError ); - return lastUpdate.filter(isDefined); } -function addChangelog(update: Update, updates?: Update[]) { - if (!updates) { - return update; - } - const index = updates.indexOf(update); - const previousUpdates = index > 0 ? updates.slice(0, index) : []; - return { ...update, previousUpdates }; -} - -function getInstalledPluginApi(type = PluginType.External) { - return getJSON('/api/plugins/installed', { f: 'category', type }); -} - -function getUpdatesPluginApi() { +export function getUpdatesPlugins() { return getJSON('/api/plugins/updates'); } -export function getInstalledPlugins( - type: PluginType = PluginType.External -): Promise<InstalledPlugin[]> { - return getInstalledPluginApi(type).then(({ plugins }) => plugins, throwGlobalError); -} - -export function getInstalledPluginsWithUpdates(): Promise<InstalledPlugin[]> { - return Promise.all([getInstalledPluginApi(), getUpdatesPluginApi()]) - .then(([installed, updates]) => - installed.plugins.map((plugin: InstalledPlugin) => { - const updatePlugin: InstalledPlugin = updates.plugins.find( - (p: InstalledPlugin) => p.key === plugin.key - ); - if (updatePlugin) { - return { - ...updatePlugin, - ...plugin, - updates: getLastUpdates(updatePlugin.updates).map((update) => - addChangelog(update, updatePlugin.updates) - ), - }; - } - return plugin; - }) - ) - .catch(throwGlobalError); -} - -export function getPluginUpdates(): Promise<InstalledPlugin[]> { - return Promise.all([getUpdatesPluginApi(), getInstalledPluginApi()]) - .then(([updates, installed]) => - updates.plugins.map((updatePlugin: InstalledPlugin) => { - const updates = getLastUpdates(updatePlugin.updates).map((update) => - addChangelog(update, updatePlugin.updates) - ); - const plugin = installed.plugins.find((p: InstalledPlugin) => p.key === updatePlugin.key); - if (plugin) { - return { - ...plugin, - ...updatePlugin, - updates, - }; - } - return { ...updatePlugin, updates }; - }) - ) - .catch(throwGlobalError); -} - export function installPlugin(data: { key: string }): Promise<void | Response> { return post('/api/plugins/install', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/App.tsx b/server/sonar-web/src/main/js/apps/marketplace/App.tsx index 52dd3a65eeb..9b5afe83d71 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -21,12 +21,7 @@ import { sortBy, uniqBy } from 'lodash'; import * as React from 'react'; import { Helmet } from 'react-helmet-async'; import { FormattedMessage } from 'react-intl'; -import { - getAvailablePlugins, - getInstalledPlugins, - getInstalledPluginsWithUpdates, - getPluginUpdates, -} from '../../api/plugins'; +import { getAvailablePlugins, getInstalledPlugins } from '../../api/plugins'; import { getValue, setSimpleSettingValue } from '../../api/settings'; import DocLink from '../../components/common/DocLink'; import Suggestions from '../../components/embed-docs-modal/Suggestions'; @@ -37,14 +32,21 @@ import { translate } from '../../helpers/l10n'; import { EditionKey } from '../../types/editions'; import { PendingPluginResult, Plugin, RiskConsent } from '../../types/plugins'; import { SettingsKey } from '../../types/settings'; -import PluginRiskConsentBox from './components/PluginRiskConsentBox'; import EditionBoxes from './EditionBoxes'; import Footer from './Footer'; import Header from './Header'; import PluginsList from './PluginsList'; import Search from './Search'; +import PluginRiskConsentBox from './components/PluginRiskConsentBox'; import './style.css'; -import { filterPlugins, parseQuery, Query, serializeQuery } from './utils'; +import { + Query, + filterPlugins, + getInstalledPluginsWithUpdates, + getPluginUpdates, + parseQuery, + serializeQuery, +} from './utils'; interface Props { currentEdition?: EditionKey; @@ -62,7 +64,7 @@ interface State { riskConsent?: RiskConsent; } -export class App extends React.PureComponent<Props, State> { +class App extends React.PureComponent<Props, State> { mounted = false; state: State = { loadingPlugins: true, plugins: [] }; diff --git a/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx b/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx index df1d0f33ba0..687a2986165 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx @@ -31,7 +31,7 @@ export interface MarketplaceAppContainerProps { appState: AppState; } -export function MarketplaceAppContainer(props: MarketplaceAppContainerProps) { +function MarketplaceAppContainer(props: MarketplaceAppContainerProps) { const { appState, location } = props; const propsToPass = { diff --git a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx index a8e7021147c..d1f094941df 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx @@ -19,6 +19,7 @@ */ import { sortBy } from 'lodash'; import * as React from 'react'; +import { translate } from '../../helpers/l10n'; import { isAvailablePlugin, isInstalledPlugin, PendingPlugin, Plugin } from '../../types/plugins'; import PluginAvailable from './components/PluginAvailable'; import PluginInstalled from './components/PluginInstalled'; @@ -53,7 +54,7 @@ export default function PluginsList(props: PluginsListProps) { const installedPlugins = plugins.filter(isInstalledPlugin); return ( <div className="boxed-group boxed-group-inner" id="marketplace-plugins"> - <ul> + <ul aria-label={translate('marketplace.page.plugins')}> {sortBy(plugins, ({ name }) => name).map((plugin) => ( <li className="panel panel-vertical" key={plugin.key}> <table className="marketplace-plugin-table"> diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/App-test.tsx deleted file mode 100644 index a55d327f73c..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/App-test.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { - getAvailablePlugins, - getInstalledPlugins, - getInstalledPluginsWithUpdates, - getPluginUpdates, -} from '../../../api/plugins'; -import { getValue, setSimpleSettingValue } from '../../../api/settings'; -import { mockLocation, mockRouter } from '../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../helpers/testUtils'; -import { EditionKey } from '../../../types/editions'; -import { RiskConsent } from '../../../types/plugins'; -import { SettingsKey } from '../../../types/settings'; -import { App } from '../App'; - -jest.mock('../../../api/plugins', () => { - const plugin = jest.requireActual('../../../helpers/mocks/plugins').mockPlugin(); - - return { - getAvailablePlugins: jest.fn().mockResolvedValue({ plugins: [plugin] }), - getInstalledPlugins: jest.fn().mockResolvedValue([]), - getInstalledPluginsWithUpdates: jest.fn().mockResolvedValue([]), - getPluginUpdates: jest.fn().mockResolvedValue([]), - }; -}); - -jest.mock('../../../api/settings', () => ({ - getValue: jest.fn().mockResolvedValue({}), - setSimpleSettingValue: jest.fn().mockResolvedValue(true), -})); - -beforeEach(jest.clearAllMocks); - -it('should render correctly', async () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot('loading'); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot('loaded'); - - wrapper.setProps({ currentEdition: EditionKey.community, standaloneMode: true }); - wrapper.setState({ riskConsent: RiskConsent.Accepted }); - - expect(wrapper).toMatchSnapshot('not readonly'); -}); - -it('should handle accepting the risk', async () => { - (getValue as jest.Mock) - .mockResolvedValueOnce({ value: RiskConsent.NotAccepted }) - .mockResolvedValueOnce({ value: RiskConsent.Accepted }); - - const wrapper = shallowRender(); - - await waitAndUpdate(wrapper); - expect(getValue).toHaveBeenCalledWith({ key: SettingsKey.PluginRiskConsent }); - - wrapper.instance().acknowledgeRisk(); - - await new Promise(setImmediate); - - expect(setSimpleSettingValue).toHaveBeenCalled(); - expect(getValue).toHaveBeenCalledWith({ key: SettingsKey.PluginRiskConsent }); - expect(wrapper.state().riskConsent).toBe(RiskConsent.Accepted); -}); - -it('should fetch plugin info', async () => { - const wrapper = shallowRender(); - - await waitAndUpdate(wrapper); - expect(getInstalledPluginsWithUpdates).toHaveBeenCalled(); - expect(getAvailablePlugins).toHaveBeenCalled(); - - wrapper.setProps({ location: mockLocation({ query: { filter: 'updates' } }) }); - await waitAndUpdate(wrapper); - expect(getPluginUpdates).toHaveBeenCalled(); - - wrapper.setProps({ location: mockLocation({ query: { filter: 'installed' } }) }); - await waitAndUpdate(wrapper); - expect(getInstalledPlugins).toHaveBeenCalled(); -}); - -function shallowRender(props: Partial<App['props']> = {}) { - return shallow<App>( - <App - currentEdition={EditionKey.developer} - fetchPendingPlugins={jest.fn()} - location={mockLocation()} - pendingPlugins={{ - installing: [], - updating: [], - removing: [], - }} - router={mockRouter()} - updateCenterActive={false} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx deleted file mode 100644 index 7c6b8b9c4d1..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { EditionKey } from '../../../types/editions'; -import EditionBoxes from '../EditionBoxes'; - -jest.mock('../../../api/navigation', () => ({ - getMarketplaceNavigation: jest.fn().mockResolvedValue({}), -})); - -it('should display the available edition boxes correctly', () => { - expect(getWrapper()).toMatchSnapshot(); -}); - -it('should display the enterprise and datacenter edition boxes', () => { - expect(getWrapper({ currentEdition: EditionKey.developer })).toMatchSnapshot(); -}); - -it('should display the datacenter edition box only', () => { - expect(getWrapper({ currentEdition: EditionKey.enterprise })).toMatchSnapshot(); -}); - -it('should not display any edition box', () => { - expect(getWrapper({ currentEdition: EditionKey.datacenter }).type()).toBeNull(); -}); - -function getWrapper(props = {}) { - return shallow(<EditionBoxes currentEdition={EditionKey.community} {...props} />); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/Footer-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/Footer-test.tsx deleted file mode 100644 index 04b0823362a..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/Footer-test.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import Footer from '../Footer'; - -it('should display the number of plugins', () => { - expect(shallow(<Footer total={3} />)).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx deleted file mode 100644 index 88245a6bc14..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { EditionKey } from '../../../types/editions'; -import Header from '../Header'; - -it('should render with installed editions', () => { - expect(shallow(<Header currentEdition={EditionKey.community} />)).toMatchSnapshot(); - expect(shallow(<Header currentEdition={EditionKey.datacenter} />)).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceApp-it.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceApp-it.tsx new file mode 100644 index 00000000000..a1302a952ee --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceApp-it.tsx @@ -0,0 +1,279 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { act } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React, { useState } from 'react'; +import { NavigationServiceMock } from '../../../api/mocks/NavigationServiceMock'; +import PluginsServiceMock from '../../../api/mocks/PluginsServiceMock'; +import SettingsServiceMock from '../../../api/mocks/SettingsServiceMock'; +import AdminContext from '../../../app/components/AdminContext'; +import { mockAppState } from '../../../helpers/testMocks'; +import { renderApp } from '../../../helpers/testReactTestingUtils'; +import { byRole, byText } from '../../../helpers/testSelector'; +import { AppState } from '../../../types/appstate'; +import { EditionKey } from '../../../types/editions'; +import { PendingPluginResult } from '../../../types/plugins'; +import { GlobalSettingKeys } from '../../../types/settings'; +import MarketplaceAppContainer from '../MarketplaceAppContainer'; + +const handler = new PluginsServiceMock(); +const settingsHandler = new SettingsServiceMock(); +const navigationHandler = new NavigationServiceMock(); + +const ui = { + title: byRole('heading', { name: 'marketplace.page.plugins' }), + deTitle: byRole('heading', { name: 'SonarQube logo Developer Edition' }), + eeTitle: byRole('heading', { name: 'SonarQube logo Enterprise Edition' }), + dceTitle: byRole('heading', { name: 'SonarQube logo Data Center Edition' }), + pluginRow: byRole('list', { name: 'marketplace.page.plugins' }).byRole('table'), + filterAll: byRole('button', { name: 'marketplace.all' }), + filterInstalled: byRole('button', { name: 'marketplace.installed' }), + filterWithUpdates: byRole('button', { name: 'marketplace.updates_only' }), + search: byRole('searchbox', { name: 'marketplace.search' }), + clearSearch: byRole('button', { name: 'clear' }), + noPluginsText: byText('marketplace.plugin_list.no_plugins', { exact: false }), + acceptTerms: byRole('checkbox', { name: 'marketplace.i_accept_the' }), + installButton: byRole('button', { name: 'marketplace.install' }), + uninstallButton: byRole('button', { name: 'marketplace.uninstall' }), + updateButton: byRole('button', { name: /marketplace.update_to_x/ }), + installPending: byText('marketplace.install_pending'), + uninstallPending: byText('marketplace.uninstall_pending'), + updatePending: byText('marketplace.update_pending'), + riskConsentMessage: byText('marketplace.risk_consent.installation'), + riskConsentButton: byRole('button', { name: 'marketplace.risk_consent.action' }), + releaseDetailsButton: byRole('button', { name: /marketplace.show_plugin_changelog/ }), + homePageLink: byRole('link', { name: 'marketplace.homepage' }), + issueTrackerLink: byRole('link', { name: 'marketplace.issue_tracker' }), + releaseNotesLink: byRole('link', { name: 'marketplace.release_notes' }), + organizationLink: byRole('link', { name: 'SonarSource' }), + bundledAvailable: byText('marketplace.available_under_commercial_license'), +}; + +beforeEach(() => { + handler.reset(); + settingsHandler.reset(); + navigationHandler.reset(); +}); + +it('should show editions', async () => { + renderMarketplaceApp(); + expect(await ui.title.find()).toBeInTheDocument(); + expect(ui.deTitle.get()).toBeInTheDocument(); + expect(ui.eeTitle.get()).toBeInTheDocument(); + expect(ui.dceTitle.get()).toBeInTheDocument(); +}); + +it('should show and filter the list', async () => { + renderMarketplaceApp(); + expect(await ui.pluginRow.findAll()).toHaveLength(6); + expect(ui.filterAll.get()).toHaveAttribute('aria-current', 'true'); + await userEvent.click(ui.filterInstalled.get()); + expect(await ui.pluginRow.findAll()).toHaveLength(3); + await userEvent.click(ui.filterWithUpdates.get()); + expect(await ui.pluginRow.findAll()).toHaveLength(2); + await userEvent.click(ui.filterAll.get()); + expect(await ui.pluginRow.findAll()).toHaveLength(6); + await userEvent.type(ui.search.get(), 'Mocked Plugin'); + expect(await ui.pluginRow.findAll()).toHaveLength(1); + await userEvent.clear(ui.search.get()); + expect(await ui.pluginRow.findAll()).toHaveLength(6); + await userEvent.type(ui.search.get(), 'Test'); + expect(await ui.pluginRow.findAll()).toHaveLength(4); + await userEvent.clear(ui.search.get()); + await userEvent.type(ui.search.get(), 'Languages'); + expect(await ui.pluginRow.findAll()).toHaveLength(1); + await userEvent.clear(ui.search.get()); + await userEvent.type(ui.search.get(), 'cantfindtheplugin'); + expect(ui.pluginRow.query()).not.toBeInTheDocument(); + expect(ui.noPluginsText.get()).toBeInTheDocument(); +}); + +it('should install, uninstall, update', async () => { + const user = userEvent.setup(); + renderMarketplaceApp(); + const rows = await ui.pluginRow.findAll(); + expect(rows).toHaveLength(6); + expect(ui.installButton.query()).not.toBeInTheDocument(); + expect(ui.uninstallButton.query()).not.toBeInTheDocument(); + expect(ui.updateButton.query()).not.toBeInTheDocument(); + expect(ui.riskConsentMessage.get()).toBeInTheDocument(); + expect(ui.riskConsentButton.get()).toBeInTheDocument(); + await act(() => user.click(ui.riskConsentButton.get())); + expect(ui.riskConsentMessage.query()).not.toBeInTheDocument(); + + expect(rows[0]).toHaveTextContent('ATest_install'); + expect(await ui.uninstallButton.find(rows[0])).toBeInTheDocument(); + expect(ui.installButton.query(rows[0])).not.toBeInTheDocument(); + expect(ui.updateButton.query(rows[0])).not.toBeInTheDocument(); + expect(ui.uninstallPending.query(rows[0])).not.toBeInTheDocument(); + await act(() => user.click(ui.uninstallButton.get(rows[0]))); + expect(await ui.uninstallPending.find(rows[0])).toBeInTheDocument(); + expect(ui.uninstallButton.query(rows[0])).not.toBeInTheDocument(); + + expect(rows[1]).toHaveTextContent('BTest_update'); + expect(ui.installButton.query(rows[1])).not.toBeInTheDocument(); + expect(ui.uninstallButton.query(rows[1])).not.toBeInTheDocument(); + expect(ui.updateButton.get(rows[1])).toBeInTheDocument(); + expect(ui.updateButton.get(rows[1])).toHaveTextContent('1.3.0'); + expect(ui.updatePending.query(rows[1])).not.toBeInTheDocument(); + await act(() => user.click(ui.updateButton.get(rows[1]))); + expect(await ui.updatePending.find(rows[1])).toBeInTheDocument(); + expect(ui.updateButton.query(rows[1])).not.toBeInTheDocument(); + + expect(rows[2]).toHaveTextContent('CFoo'); + expect(ui.installButton.get(rows[2])).toBeInTheDocument(); + expect(ui.uninstallButton.query(rows[2])).not.toBeInTheDocument(); + expect(ui.updateButton.query(rows[2])).not.toBeInTheDocument(); + expect(ui.installPending.query(rows[2])).not.toBeInTheDocument(); + await act(() => user.click(ui.installButton.get(rows[2]))); + expect(await ui.installPending.find(rows[2])).toBeInTheDocument(); + expect(ui.installButton.query(rows[2])).not.toBeInTheDocument(); + + expect(rows[3]).toHaveTextContent('DTest'); + expect(ui.installButton.query(rows[2])).not.toBeInTheDocument(); + expect(ui.bundledAvailable.get(rows[3])).toBeInTheDocument(); + + expect(rows[4]).toHaveTextContent('Sonar Foo'); + expect(ui.installButton.get(rows[4])).toBeInTheDocument(); + expect(ui.installButton.get(rows[4])).toBeDisabled(); + expect(ui.acceptTerms.get(rows[4])).toBeInTheDocument(); + expect(ui.acceptTerms.get(rows[4])).not.toBeChecked(); + await user.click(ui.acceptTerms.get(rows[4])); + expect(ui.installButton.get(rows[4])).toBeEnabled(); + await act(() => user.click(ui.installButton.get(rows[4]))); + expect(await ui.installPending.find(rows[4])).toBeInTheDocument(); + expect(ui.installButton.query(rows[4])).not.toBeInTheDocument(); + + expect(rows[5]).toHaveTextContent('ZTest'); + expect(ui.installButton.query(rows[5])).not.toBeInTheDocument(); + expect(ui.uninstallButton.query(rows[5])).toBeInTheDocument(); + expect(ui.updateButton.get(rows[5])).toBeInTheDocument(); + expect(ui.updateButton.get(rows[5])).toHaveTextContent('1.2.0'); + expect(ui.updatePending.query(rows[5])).not.toBeInTheDocument(); + await act(() => user.click(ui.updateButton.get(rows[5]))); + expect(await ui.updatePending.find(rows[5])).toBeInTheDocument(); + expect(ui.uninstallButton.query(rows[5])).not.toBeInTheDocument(); + expect(ui.updateButton.query(rows[5])).not.toBeInTheDocument(); +}); + +it('should show details on the row', async () => { + const user = userEvent.setup(); + renderMarketplaceApp(); + const rows = await ui.pluginRow.findAll(); + const row = rows[2]; + expect(row).toHaveTextContent('CFooLanguagesDescription1.0.0release description'); + expect(ui.homePageLink.get(row)).toBeInTheDocument(); + expect(ui.issueTrackerLink.get(row)).toBeInTheDocument(); + expect(ui.organizationLink.get(row)).toBeInTheDocument(); + expect(ui.releaseDetailsButton.get(row)).toBeInTheDocument(); + expect(ui.releaseNotesLink.query(row)).not.toBeInTheDocument(); + await user.click(ui.releaseDetailsButton.get(row)); + expect(ui.releaseNotesLink.get(row)).toBeInTheDocument(); + + expect(ui.homePageLink.query(rows[0])).not.toBeInTheDocument(); + expect(ui.issueTrackerLink.query(rows[0])).not.toBeInTheDocument(); + expect(ui.organizationLink.query(rows[0])).not.toBeInTheDocument(); + + const rowWithMultipleUpdates = rows[1]; + expect(rowWithMultipleUpdates).toHaveTextContent( + '1.2.0marketplace._installedmarketplace.updates:1.3.0' + ); + await user.click(ui.releaseDetailsButton.get(rowWithMultipleUpdates)); + expect(ui.releaseNotesLink.getAll(rowWithMultipleUpdates)).toHaveLength(2); +}); + +it.each([ + [EditionKey.developer, { de: false, ee: true, dce: true }], + [EditionKey.enterprise, { de: false, ee: false, dce: true }], + [EditionKey.datacenter, { de: false, ee: false, dce: false }], +])( + 'should not allow installations on editions higher than community', + async (edition, showTitles) => { + renderMarketplaceApp({ edition }); + const rows = await ui.pluginRow.findAll(); + expect(rows).toHaveLength(6); + expect(ui.updateButton.query()).not.toBeInTheDocument(); + expect(ui.installButton.query()).not.toBeInTheDocument(); + expect(ui.uninstallButton.query()).not.toBeInTheDocument(); + + Object.entries(showTitles).forEach( + ([key, value]: [key: 'de' | 'ee' | 'dce', value: boolean]) => { + // eslint-disable-next-line jest/no-conditional-in-test + if (value) { + // eslint-disable-next-line jest/no-conditional-expect + expect(ui[`${key}Title`].get()).toBeInTheDocument(); + } else { + // eslint-disable-next-line jest/no-conditional-expect + expect(ui[`${key}Title`].query()).not.toBeInTheDocument(); + } + } + ); + + expect(ui.riskConsentMessage.query()).not.toBeInTheDocument(); + } +); + +describe('accessibility', () => { + it('should be accessible', async () => { + const user = userEvent.setup(); + renderMarketplaceApp(); + const row = (await ui.pluginRow.findAll())[1]; + await expect(document.body).toHaveNoA11yViolations(); + await act(() => user.click(ui.riskConsentButton.get())); + await act(() => user.click(ui.releaseDetailsButton.get(row))); + await expect(document.body).toHaveNoA11yViolations(); + }); +}); + +function renderMarketplaceApp(appStateOverrides: Partial<AppState> = {}) { + function Wrapper() { + const [pendingPlugins, setPendingPlugins] = useState<PendingPluginResult>({ + installing: [], + removing: [], + updating: [], + }); + const fetchPendingPlugins = async () => { + setPendingPlugins(await handler.handleGetPendingPlugins()); + }; + return ( + <AdminContext.Provider + value={{ + fetchSystemStatus: () => {}, + fetchPendingPlugins, + pendingPlugins, + systemStatus: null as any, + }} + > + <MarketplaceAppContainer /> + </AdminContext.Provider> + ); + } + + return renderApp('admin/marketplace', <Wrapper />, { + appState: mockAppState({ + edition: EditionKey.community, + standalone: true, + settings: { + [GlobalSettingKeys.UpdatecenterActivated]: 'true', + }, + ...appStateOverrides, + }), + }); +} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx deleted file mode 100644 index e923c23f9be..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import React from 'react'; -import { mockAppState, mockLocation } from '../../../helpers/testMocks'; -import { GlobalSettingKeys } from '../../../types/settings'; -import { EditionKey } from '../../../types/editions'; -import { MarketplaceAppContainer, MarketplaceAppContainerProps } from '../MarketplaceAppContainer'; - -it('should render correctly', () => { - expect(shallowRender().dive()).toMatchSnapshot('default'); - expect( - shallowRender({ - appState: mockAppState({ - settings: { - [GlobalSettingKeys.UpdatecenterActivated]: 'true', - }, - }), - }).dive() - ).toMatchSnapshot('update center active'); -}); - -function shallowRender(overrides: Partial<MarketplaceAppContainerProps> = {}) { - return shallow<MarketplaceAppContainerProps>( - <MarketplaceAppContainer - appState={mockAppState({ edition: EditionKey.community, standalone: true })} - location={mockLocation()} - {...overrides} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PluginUpdates-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/PluginUpdates-test.tsx deleted file mode 100644 index 7f9cf9c1d0d..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PluginUpdates-test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import PluginUpdates, { PluginUpdatesProps } from '../components/PluginUpdates'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect( - shallowRender({ - updates: [ - { - requires: [], - status: '', - release: { date: '2012-02-10', version: '1.3' }, - }, - { - requires: [], - status: '', - release: { date: '2012-02-01', version: '1.1' }, - }, - { - requires: [], - status: '', - release: { date: '2012-02-02', version: '1.2' }, - }, - ], - }) - ).toMatchSnapshot('with status'); -}); - -function shallowRender(props: Partial<PluginUpdatesProps> = {}) { - return shallow<PluginUpdatesProps>(<PluginUpdates pluginName="Xoo" {...props} />); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PluginsList-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/PluginsList-test.tsx deleted file mode 100644 index 4046e2fbd0b..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PluginsList-test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockAvailablePlugin, mockPendingPlugin } from '../../../helpers/mocks/plugins'; -import PluginsList, { PluginsListProps } from '../PluginsList'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect( - shallowRender({ - pending: { - installing: [mockPendingPlugin({ key: 'sonar-foo' })], - updating: [mockPendingPlugin({ key: 'sonar-bar' })], - removing: [mockPendingPlugin({ key: 'sonar-baz' })], - }, - }) - ).toMatchSnapshot('with status'); -}); - -function shallowRender(props: Partial<PluginsListProps> = {}) { - return shallow<PluginsListProps>( - <PluginsList - plugins={[ - mockAvailablePlugin({ key: 'sonar-foo' }), - mockAvailablePlugin({ key: 'sonar-bar', name: 'Sonar Bar' }), - mockAvailablePlugin({ key: 'sonar-baz', name: 'Sonar Baz' }), - ]} - pending={{ installing: [], updating: [], removing: [] }} - readOnly={false} - refreshPending={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/App-test.tsx.snap deleted file mode 100644 index dddad9f865d..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/App-test.tsx.snap +++ /dev/null @@ -1,254 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: loaded 1`] = ` -<main - className="page page-limited" - id="marketplace-page" -> - <Suggestions - suggestions="marketplace" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="marketplace.page" - /> - <Header - currentEdition="developer" - /> - <EditionBoxes - currentEdition="developer" - /> - <header - className="page-header" - > - <h2 - className="page-title" - > - marketplace.page.plugins - </h2> - <div - className="page-description" - > - <p> - marketplace.page.plugins.description - </p> - <Alert - className="spacer-top" - variant="info" - > - <FormattedMessage - defaultMessage="marketplace.page.plugins.description2" - id="marketplace.page.plugins.description2" - values={ - { - "link": <DocLink - to="/instance-administration/marketplace/" - > - marketplace.page.plugins.description2.link - </DocLink>, - } - } - /> - </Alert> - </div> - </header> - <PluginRiskConsentBox - acknowledgeRisk={[Function]} - currentEdition="developer" - /> - <Search - query={ - { - "filter": "all", - "search": "", - } - } - updateCenterActive={false} - updateQuery={[Function]} - /> - <DeferredSpinner - loading={false} - > - <PluginsList - pending={ - { - "installing": [], - "removing": [], - "updating": [], - } - } - plugins={ - [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - ] - } - readOnly={true} - refreshPending={[MockFunction]} - /> - <Footer - total={1} - /> - </DeferredSpinner> -</main> -`; - -exports[`should render correctly: loading 1`] = ` -<main - className="page page-limited" - id="marketplace-page" -> - <Suggestions - suggestions="marketplace" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="marketplace.page" - /> - <Header - currentEdition="developer" - /> - <EditionBoxes - currentEdition="developer" - /> - <header - className="page-header" - > - <h2 - className="page-title" - > - marketplace.page.plugins - </h2> - <div - className="page-description" - > - <p> - marketplace.page.plugins.description - </p> - <Alert - className="spacer-top" - variant="info" - > - <FormattedMessage - defaultMessage="marketplace.page.plugins.description2" - id="marketplace.page.plugins.description2" - values={ - { - "link": <DocLink - to="/instance-administration/marketplace/" - > - marketplace.page.plugins.description2.link - </DocLink>, - } - } - /> - </Alert> - </div> - </header> - <PluginRiskConsentBox - acknowledgeRisk={[Function]} - currentEdition="developer" - /> - <Search - query={ - { - "filter": "all", - "search": "", - } - } - updateCenterActive={false} - updateQuery={[Function]} - /> - <DeferredSpinner - loading={true} - > - marketplace.plugin_list.no_plugins.all - </DeferredSpinner> -</main> -`; - -exports[`should render correctly: not readonly 1`] = ` -<main - className="page page-limited" - id="marketplace-page" -> - <Suggestions - suggestions="marketplace" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="marketplace.page" - /> - <Header - currentEdition="community" - /> - <EditionBoxes - currentEdition="community" - /> - <header - className="page-header" - > - <h2 - className="page-title" - > - marketplace.page.plugins - </h2> - <div - className="page-description" - > - <p> - marketplace.page.plugins.description - </p> - </div> - </header> - <PluginRiskConsentBox - acknowledgeRisk={[Function]} - currentEdition="community" - riskConsent="ACCEPTED" - /> - <Search - query={ - { - "filter": "all", - "search": "", - } - } - updateCenterActive={false} - updateQuery={[Function]} - /> - <DeferredSpinner - loading={false} - > - <PluginsList - pending={ - { - "installing": [], - "removing": [], - "updating": [], - } - } - plugins={ - [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - ] - } - readOnly={false} - refreshPending={[MockFunction]} - /> - <Footer - total={1} - /> - </DeferredSpinner> -</main> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap deleted file mode 100644 index 9e68295f6e4..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap +++ /dev/null @@ -1,94 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display the available edition boxes correctly 1`] = ` -<div - className="spacer-bottom marketplace-editions" -> - <EditionBox - currentEdition="community" - edition={ - { - "downloadProperty": "downloadDeveloperUrl", - "homeUrl": "https://www.sonarsource.com/plans-and-pricing/developer/", - "key": "developer", - "name": "Developer Edition", - } - } - key="developer" - /> - <EditionBox - currentEdition="community" - edition={ - { - "downloadProperty": "downloadEnterpriseUrl", - "homeUrl": "https://www.sonarsource.com/plans-and-pricing/enterprise/", - "key": "enterprise", - "name": "Enterprise Edition", - } - } - key="enterprise" - /> - <EditionBox - currentEdition="community" - edition={ - { - "downloadProperty": "downloadDatacenterUrl", - "homeUrl": "https://www.sonarsource.com/plans-and-pricing/data-center/", - "key": "datacenter", - "name": "Data Center Edition", - } - } - key="datacenter" - /> -</div> -`; - -exports[`should display the datacenter edition box only 1`] = ` -<div - className="spacer-bottom marketplace-editions" -> - <EditionBox - currentEdition="enterprise" - edition={ - { - "downloadProperty": "downloadDatacenterUrl", - "homeUrl": "https://www.sonarsource.com/plans-and-pricing/data-center/", - "key": "datacenter", - "name": "Data Center Edition", - } - } - key="datacenter" - /> -</div> -`; - -exports[`should display the enterprise and datacenter edition boxes 1`] = ` -<div - className="spacer-bottom marketplace-editions" -> - <EditionBox - currentEdition="developer" - edition={ - { - "downloadProperty": "downloadEnterpriseUrl", - "homeUrl": "https://www.sonarsource.com/plans-and-pricing/enterprise/", - "key": "enterprise", - "name": "Enterprise Edition", - } - } - key="enterprise" - /> - <EditionBox - currentEdition="developer" - edition={ - { - "downloadProperty": "downloadDatacenterUrl", - "homeUrl": "https://www.sonarsource.com/plans-and-pricing/data-center/", - "key": "datacenter", - "name": "Data Center Edition", - } - } - key="datacenter" - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/Footer-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/Footer-test.tsx.snap deleted file mode 100644 index 164d3bd00e6..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/Footer-test.tsx.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display the number of plugins 1`] = ` -<footer - className="spacer-top note text-center" -> - x_show.3 -</footer> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/Header-test.tsx.snap deleted file mode 100644 index 83e8c3b7b33..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/Header-test.tsx.snap +++ /dev/null @@ -1,47 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render with installed editions 1`] = ` -<header - className="page-header" - id="marketplace-header" -> - <h2 - className="page-title" - > - marketplace.page - </h2> - <h3 - className="page-description" - > - marketplace.page.you_are_running.community - </h3> - <p - className="page-description" - > - marketplace.page.description - </p> -</header> -`; - -exports[`should render with installed editions 2`] = ` -<header - className="page-header" - id="marketplace-header" -> - <h2 - className="page-title" - > - marketplace.page - </h2> - <h3 - className="page-description" - > - marketplace.page.you_are_running.datacenter - </h3> - <p - className="page-description" - > - marketplace.page.description_best_edition - </p> -</header> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap deleted file mode 100644 index 6a51a26919c..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap +++ /dev/null @@ -1,52 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = ` -<withRouter(App) - currentEdition="community" - fetchPendingPlugins={[Function]} - location={ - { - "hash": "", - "key": "key", - "pathname": "/path", - "query": {}, - "search": "", - "state": {}, - } - } - pendingPlugins={ - { - "installing": [], - "removing": [], - "updating": [], - } - } - standaloneMode={true} - updateCenterActive={false} -/> -`; - -exports[`should render correctly: update center active 1`] = ` -<withRouter(App) - currentEdition="community" - fetchPendingPlugins={[Function]} - location={ - { - "hash": "", - "key": "key", - "pathname": "/path", - "query": {}, - "search": "", - "state": {}, - } - } - pendingPlugins={ - { - "installing": [], - "removing": [], - "updating": [], - } - } - updateCenterActive={true} -/> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PluginUpdates-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PluginUpdates-test.tsx.snap deleted file mode 100644 index a23a7b78f58..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PluginUpdates-test.tsx.snap +++ /dev/null @@ -1,78 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = `""`; - -exports[`should render correctly: with status 1`] = ` -<li - className="spacer-top" -> - <strong> - marketplace.updates - : - </strong> - <ul - className="little-spacer-top" - > - <PluginUpdateItem - key="1.3" - pluginName="Xoo" - release={ - { - "date": "2012-02-10", - "version": "1.3", - } - } - update={ - { - "release": { - "date": "2012-02-10", - "version": "1.3", - }, - "requires": [], - "status": "", - } - } - /> - <PluginUpdateItem - key="1.1" - pluginName="Xoo" - release={ - { - "date": "2012-02-01", - "version": "1.1", - } - } - update={ - { - "release": { - "date": "2012-02-01", - "version": "1.1", - }, - "requires": [], - "status": "", - } - } - /> - <PluginUpdateItem - key="1.2" - pluginName="Xoo" - release={ - { - "date": "2012-02-02", - "version": "1.2", - } - } - update={ - { - "release": { - "date": "2012-02-02", - "version": "1.2", - }, - "requires": [], - "status": "", - } - } - /> - </ul> -</li> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PluginsList-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PluginsList-test.tsx.snap deleted file mode 100644 index b7e62907a75..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PluginsList-test.tsx.snap +++ /dev/null @@ -1,204 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = ` -<div - className="boxed-group boxed-group-inner" - id="marketplace-plugins" -> - <ul> - <li - className="panel panel-vertical" - key="sonar-bar" - > - <table - className="marketplace-plugin-table" - > - <tbody> - <PluginAvailable - installedPlugins={[]} - plugin={ - { - "key": "sonar-bar", - "name": "Sonar Bar", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - readOnly={false} - refreshPending={[MockFunction]} - /> - </tbody> - </table> - </li> - <li - className="panel panel-vertical" - key="sonar-baz" - > - <table - className="marketplace-plugin-table" - > - <tbody> - <PluginAvailable - installedPlugins={[]} - plugin={ - { - "key": "sonar-baz", - "name": "Sonar Baz", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - readOnly={false} - refreshPending={[MockFunction]} - /> - </tbody> - </table> - </li> - <li - className="panel panel-vertical" - key="sonar-foo" - > - <table - className="marketplace-plugin-table" - > - <tbody> - <PluginAvailable - installedPlugins={[]} - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - readOnly={false} - refreshPending={[MockFunction]} - /> - </tbody> - </table> - </li> - </ul> -</div> -`; - -exports[`should render correctly: with status 1`] = ` -<div - className="boxed-group boxed-group-inner" - id="marketplace-plugins" -> - <ul> - <li - className="panel panel-vertical" - key="sonar-bar" - > - <table - className="marketplace-plugin-table" - > - <tbody> - <PluginAvailable - installedPlugins={[]} - plugin={ - { - "key": "sonar-bar", - "name": "Sonar Bar", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - readOnly={false} - refreshPending={[MockFunction]} - status="updating" - /> - </tbody> - </table> - </li> - <li - className="panel panel-vertical" - key="sonar-baz" - > - <table - className="marketplace-plugin-table" - > - <tbody> - <PluginAvailable - installedPlugins={[]} - plugin={ - { - "key": "sonar-baz", - "name": "Sonar Baz", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - readOnly={false} - refreshPending={[MockFunction]} - status="removing" - /> - </tbody> - </table> - </li> - <li - className="panel panel-vertical" - key="sonar-foo" - > - <table - className="marketplace-plugin-table" - > - <tbody> - <PluginAvailable - installedPlugins={[]} - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - readOnly={false} - refreshPending={[MockFunction]} - status="installing" - /> - </tbody> - </table> - </li> - </ul> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx index 487b50dabe1..aff28bf3c4f 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx @@ -122,7 +122,7 @@ export default class PluginActions extends React.PureComponent<Props, State> { return ( <div className="it__js-actions"> {isAvailablePlugin(plugin) && plugin.termsAndConditionsUrl && ( - <p className="little-spacer-bottom"> + <div className="little-spacer-bottom"> <Checkbox checked={this.state.acceptTerms} className="js-terms" @@ -141,7 +141,7 @@ export default class PluginActions extends React.PureComponent<Props, State> { > {translate('marketplace.terms_and_conditions')} </a> - </p> + </div> )} {loading && <i className="spinner spacer-right little-spacer-top little-spacer-bottom" />} {isInstalledPlugin(plugin) && ( diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/PluginChangeLog.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/PluginChangeLog.tsx index 4354d987118..0c0296256df 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/PluginChangeLog.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/PluginChangeLog.tsx @@ -31,7 +31,7 @@ export interface Props { export default function PluginChangeLog({ release, update }: Props) { return ( <div className="abs-width-300"> - <h6>{translate('changelog')}</h6> + <b className="sw-leading-6">{translate('changelog')}</b> <ul className="js-plugin-changelog-list"> {update.previousUpdates && sortBy(update.previousUpdates, (prevUpdate) => prevUpdate.release?.date).map( diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/PluginUpdateItem.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/PluginUpdateItem.tsx index d8e50710ab6..4ba13f54729 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/PluginUpdateItem.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/PluginUpdateItem.tsx @@ -29,49 +29,22 @@ interface Props { release: Release; } -interface State { - changelogOpen: boolean; -} - -export default class PluginUpdateItem extends React.PureComponent<Props, State> { - state: State = { changelogOpen: false }; - - handleChangelogClick = (event: React.SyntheticEvent<HTMLButtonElement>) => { - event.preventDefault(); - event.stopPropagation(); - this.toggleChangelog(); - }; - - toggleChangelog = (show?: boolean) => { - if (show !== undefined) { - this.setState({ changelogOpen: show }); - } else { - this.setState((state) => ({ changelogOpen: !state.changelogOpen })); - } - }; - - render() { - const { release, update } = this.props; - return ( - <li className="display-flex-row little-spacer-bottom" key={release.version}> - <div className="pull-left spacer-right"> - {update.status === 'COMPATIBLE' ? ( - <span className="js-update-version badge badge-success">{release.version}</span> - ) : ( - <Tooltip overlay={translate('marketplace.update_status', update.status)}> - <span className="js-update-version badge badge-warning">{release.version}</span> - </Tooltip> - )} - </div> - <div> - {release.description} - <PluginChangeLogButton - pluginName={this.props.pluginName} - release={release} - update={update} - /> - </div> - </li> - ); - } +export default function PluginUpdateItem({ release, update, pluginName }: Props) { + return ( + <li className="display-flex-row little-spacer-bottom" key={release.version}> + <div className="pull-left spacer-right"> + {update.status === 'COMPATIBLE' ? ( + <span className="js-update-version badge badge-success">{release.version}</span> + ) : ( + <Tooltip overlay={translate('marketplace.update_status', update.status)}> + <span className="js-update-version badge badge-warning">{release.version}</span> + </Tooltip> + )} + </div> + <div> + {release.description} + <PluginChangeLogButton pluginName={pluginName} release={release} update={update} /> + </div> + </li> + ); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx deleted file mode 100644 index 037d0606860..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { getEdition } from '../../../../helpers/editions'; -import { EditionKey } from '../../../../types/editions'; -import EditionBox from '../EditionBox'; - -it('should display the edition', () => { - expect( - shallow( - <EditionBox - currentEdition={EditionKey.community} - edition={getEdition(EditionKey.developer)} - ncloc={1000} - serverId="serverId" - /> - ) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginActions-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginActions-test.tsx deleted file mode 100644 index 37fab4d8e0c..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginActions-test.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { AvailablePlugin, InstalledPlugin } from '../../../../types/plugins'; -import PluginActions from '../PluginActions'; - -const installedPlugin: InstalledPlugin = { - key: 'foo', - name: 'Foo', - filename: 'foo.zip', - hash: '', - implementationBuild: '', - sonarLintSupported: true, - termsAndConditionsUrl: 'https://url', - updatedAt: 1, - updates: [{ status: 'COMPATIBLE', requires: [] }], - version: '7.7', -}; - -const availablePlugin: AvailablePlugin = { - key: 'foo', - name: 'Foo', - release: { version: '7.7', date: 'date' }, - termsAndConditionsUrl: 'https://url', - update: { status: 'COMPATIBLE', requires: [] }, -}; - -it('should render installed plugin correctly', () => { - expect(shallowRender()).toMatchSnapshot(); - expect(shallowRender({ plugin: { ...installedPlugin, editionBundled: true } })).toMatchSnapshot(); -}); - -it('should render available plugin correctly', () => { - expect(shallowRender({ plugin: availablePlugin })).toMatchSnapshot(); - expect(shallowRender({ plugin: { ...availablePlugin, editionBundled: true } })).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<PluginActions['props']> = {}) { - return shallow(<PluginActions plugin={installedPlugin} refreshPending={jest.fn()} {...props} />); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginAvailable-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginAvailable-test.tsx deleted file mode 100644 index b0e18d39052..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginAvailable-test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { - mockAvailablePlugin, - mockInstalledPlugin, - mockPlugin, - mockUpdate, -} from '../../../../helpers/mocks/plugins'; -import PluginAvailable, { PluginAvailableProps } from '../PluginAvailable'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender({ readOnly: true })).toMatchSnapshot('read only'); - expect( - shallowRender({ - plugin: mockAvailablePlugin({ - update: mockUpdate({ requires: [mockPlugin()] }), - }), - }) - ).toMatchSnapshot('has requirements'); - const installed = mockInstalledPlugin({ key: 'sonar-bar', name: 'Sonar Bar' }); - expect( - shallowRender({ - installedPlugins: [installed], - plugin: mockAvailablePlugin({ - update: mockUpdate({ - requires: [mockPlugin(), installed], - }), - }), - }) - ).toMatchSnapshot('has requirements, some of them already met'); -}); - -function shallowRender(props: Partial<PluginAvailableProps> = {}) { - return shallow<PluginAvailableProps>( - <PluginAvailable - installedPlugins={[]} - plugin={mockAvailablePlugin()} - readOnly={false} - refreshPending={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginChangeLog-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginChangeLog-test.tsx deleted file mode 100644 index a3175a05368..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginChangeLog-test.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import PluginChangeLog, { Props } from '../PluginChangeLog'; - -it('should render correctly', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<Props> = {}) { - return shallow( - <PluginChangeLog - release={{ - version: '0.11', - date: '2018-11-05', - description: 'Change version description', - changeLogUrl: 'https://my.change.log/url', - }} - update={{ - previousUpdates: [ - { - release: { - version: '0.11', - date: '2018-06-08', - description: 'Change version description', - changeLogUrl: 'https://my.change.log/url', - }, - requires: [], - status: 'COMPATIBLE', - }, - { - release: { - version: '0.10', - date: '2018-06-05', - description: 'Change version description', - changeLogUrl: 'https://my.change.log/url', - }, - requires: [], - status: 'COMPATIBLE', - }, - ], - requires: [{ key: 'java', name: 'SonarJava', description: 'Code Analyzer for Java' }], - status: 'COMPATIBLE', - }} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginDescription-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginDescription-test.tsx deleted file mode 100644 index 42192e3bc3c..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginDescription-test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import PluginDescription from '../PluginDescription'; - -it('should display the description and category', () => { - expect(getWrapper()).toMatchSnapshot(); -}); - -it('should not display any category', () => { - expect( - getWrapper({ plugin: { key: 'foo', name: 'Foo', description: 'foo description' } }) - ).toMatchSnapshot(); -}); - -function getWrapper(props = {}) { - return shallow( - <PluginDescription - plugin={{ - key: 'foo', - name: 'Foo', - description: 'foo description', - category: 'foocategory', - }} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginLicense-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginLicense-test.tsx deleted file mode 100644 index ecda6616b48..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginLicense-test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import PluginLicense from '../PluginLicense'; - -it('should display the license field', () => { - expect(shallow(<PluginLicense license="SonarSource license" />)).toMatchSnapshot(); -}); - -it('should not display anything', () => { - expect(shallow(<PluginLicense />).type()).toBeNull(); -}); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginOrganization-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginOrganization-test.tsx deleted file mode 100644 index a9d3457e6ad..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginOrganization-test.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import PluginOrganization, { PluginOrganizationProps } from '../PluginOrganization'; - -it('should render correctly', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); -}); - -it('should render correctly with no organization name', () => { - const wrapper = shallowRender({ - plugin: { key: 'test', name: 'test', organizationName: undefined }, - }); - expect(wrapper.type()).toBeNull(); -}); - -function shallowRender(props?: Partial<PluginOrganizationProps>) { - return shallow( - <PluginOrganization - plugin={{ key: 'test', name: 'test', organizationName: 'org', organizationUrl: 'org_url' }} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginRiskConsentBox-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginRiskConsentBox-test.tsx deleted file mode 100644 index 04eca18d869..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginRiskConsentBox-test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { EditionKey } from '../../../../types/editions'; -import { RiskConsent } from '../../../../types/plugins'; -import PluginRiskConsentBox, { PluginRiskConsentBoxProps } from '../PluginRiskConsentBox'; - -it.each([[undefined], [RiskConsent.Accepted], [RiskConsent.NotAccepted], [RiskConsent.Required]])( - 'should render correctly for risk consent %s', - (riskConsent?: RiskConsent) => { - expect(shallowRender({ riskConsent })).toMatchSnapshot(); - } -); - -it('should render correctly for community edition', () => { - expect(shallowRender({ currentEdition: EditionKey.community })).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<PluginRiskConsentBoxProps> = {}) { - return shallow(<PluginRiskConsentBox acknowledgeRisk={jest.fn()} {...props} />); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginUrls-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginUrls-test.tsx deleted file mode 100644 index 4bef5df74a8..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginUrls-test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import PluginUrls from '../PluginUrls'; - -it('should display the urls', () => { - expect(getWrapper()).toMatchSnapshot(); -}); - -it('should display only one url', () => { - expect(getWrapper({ issueTrackerUrl: undefined })).toMatchSnapshot(); -}); - -it('should not display anything', () => { - expect(getWrapper({ homepageUrl: undefined, issueTrackerUrl: undefined }).type()).toBeNull(); -}); - -function getWrapper(plugin = {}) { - return shallow( - <PluginUrls - plugin={{ - key: 'foo', - name: 'Foo', - description: 'foo description', - homepageUrl: 'homepageurl', - issueTrackerUrl: 'issuetrackerurl', - ...plugin, - }} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap deleted file mode 100644 index f239229c3d1..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap +++ /dev/null @@ -1,83 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display the edition 1`] = ` -<div - className="boxed-group boxed-group-inner marketplace-edition" -> - <div - className="markdown" - > - <div - className="markdown-content" - > - <div> - <h3 - id="developer-edition" - > - <img - alt="SonarQube logo" - className="max-width-100 little-spacer-right" - src="/images/embed-doc/sq-icon.svg" - /> - Developer Edition - </h3> - <p> - <em> - Built for Developers by Developers - </em> - </p> - <p> - Community Edition functionality plus: - </p> - <ul> - <li> - PR / MR decoration & Quality Gate - <img - alt="GitHub" - className="little-spacer-left max-width-100" - src="/images/alm/github.svg" - /> - <img - alt="GitLab" - className="little-spacer-left max-width-100" - src="/images/alm/gitlab.svg" - /> - <img - alt="Azure DevOps" - className="little-spacer-left max-width-100" - src="/images/alm/azure.svg" - /> - <img - alt="Bitbucket" - className="little-spacer-left max-width-100" - src="/images/alm/bitbucket.svg" - /> - </li> - <li> - Taint analysis / Injection flaw detection for Java, C#, PHP, Python, JS & TS - </li> - <li> - Branch analysis - </li> - <li> - Project aggregation - </li> - <li> - Additional languages: C, C++, Obj-C, PS/SQL, ABAP, TSQL & Swift - </li> - </ul> - </div> - </div> - </div> - <div - className="marketplace-edition-action spacer-top" - > - <ForwardRef(Link) - target="_blank" - to="https://www.sonarsource.com/plans-and-pricing/developer/?ncloc=1000&serverId=serverId&sourceEdition=community" - > - marketplace.request_free_trial - </ForwardRef(Link)> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginActions-test.tsx.snap deleted file mode 100644 index f0cbba99385..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginActions-test.tsx.snap +++ /dev/null @@ -1,116 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render available plugin correctly 1`] = ` -<div - className="it__js-actions" -> - <p - className="little-spacer-bottom" - > - <Checkbox - checked={false} - className="js-terms" - id="plugin-terms-foo" - onCheck={[Function]} - thirdState={false} - > - <label - className="little-spacer-left" - htmlFor="plugin-terms-foo" - > - marketplace.i_accept_the - </label> - </Checkbox> - <a - className="js-plugin-terms nowrap little-spacer-left" - href="https://url" - rel="noopener noreferrer" - target="_blank" - > - marketplace.terms_and_conditions - </a> - </p> - <Tooltip - overlay="marketplace.requires_restart" - > - <Button - className="js-install" - disabled={true} - onClick={[Function]} - > - marketplace.install - </Button> - </Tooltip> -</div> -`; - -exports[`should render available plugin correctly 2`] = ` -<div - className="it__js-actions" -> - <div> - <p - className="little-spacer-bottom" - > - marketplace.available_under_commercial_license - </p> - </div> -</div> -`; - -exports[`should render installed plugin correctly 1`] = ` -<div - className="it__js-actions" -> - <PluginUpdateButton - disabled={false} - key="0" - onClick={[Function]} - update={ - { - "requires": [], - "status": "COMPATIBLE", - } - } - /> - <Tooltip - overlay="marketplace.requires_restart" - > - <Button - className="js-uninstall button-red little-spacer-left" - disabled={false} - onClick={[Function]} - > - marketplace.uninstall - </Button> - </Tooltip> -</div> -`; - -exports[`should render installed plugin correctly 2`] = ` -<div - className="it__js-actions" -> - <p> - <CheckIcon - className="little-spacer-right" - /> - marketplace.installed - </p> - <div - className="spacer-top" - > - <PluginUpdateButton - disabled={false} - key="0" - onClick={[Function]} - update={ - { - "requires": [], - "status": "COMPATIBLE", - } - } - /> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginAvailable-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginAvailable-test.tsx.snap deleted file mode 100644 index 51aa9ac7916..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginAvailable-test.tsx.snap +++ /dev/null @@ -1,550 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = ` -<tr> - <PluginDescription - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - /> - <td - className="text-top big-spacer-right" - > - <ul> - <li - className="display-flex-row little-spacer-bottom" - > - <div - className="pull-left spacer-right" - > - <span - className="badge badge-success" - > - 8.2 - </span> - </div> - <div> - <PluginChangeLogButton - pluginName="Sonar Foo" - release={ - { - "date": "2020-01-01", - "version": "8.2", - } - } - update={ - { - "requires": [], - "status": "available", - } - } - /> - </div> - </li> - </ul> - </td> - <td - className="text-top width-20" - > - <ul> - <PluginUrls - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - /> - <PluginLicense /> - <PluginOrganization - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - /> - </ul> - </td> - <PluginStatus - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - refreshPending={[MockFunction]} - /> -</tr> -`; - -exports[`should render correctly: has requirements 1`] = ` -<tr> - <PluginDescription - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - ], - "status": "available", - }, - } - } - /> - <td - className="text-top big-spacer-right" - > - <ul> - <li - className="display-flex-row little-spacer-bottom" - > - <div - className="pull-left spacer-right" - > - <span - className="badge badge-success" - > - 8.2 - </span> - </div> - <div> - <PluginChangeLogButton - pluginName="Sonar Foo" - release={ - { - "date": "2020-01-01", - "version": "8.2", - } - } - update={ - { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - ], - "status": "available", - } - } - /> - <p - className="little-spacer-top" - > - <strong> - marketplace.installing_this_plugin_will_also_install_x.Sonar Foo - </strong> - </p> - </div> - </li> - </ul> - </td> - <td - className="text-top width-20" - > - <ul> - <PluginUrls - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - ], - "status": "available", - }, - } - } - /> - <PluginLicense /> - <PluginOrganization - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - ], - "status": "available", - }, - } - } - /> - </ul> - </td> - <PluginStatus - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - ], - "status": "available", - }, - } - } - refreshPending={[MockFunction]} - /> -</tr> -`; - -exports[`should render correctly: has requirements, some of them already met 1`] = ` -<tr> - <PluginDescription - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - { - "filename": "sonar-bar-1.0.jar", - "hash": "hash", - "implementationBuild": "1.0.0.1234", - "key": "sonar-bar", - "name": "Sonar Bar", - "sonarLintSupported": false, - "updatedAt": 100, - "version": "1.0", - }, - ], - "status": "available", - }, - } - } - /> - <td - className="text-top big-spacer-right" - > - <ul> - <li - className="display-flex-row little-spacer-bottom" - > - <div - className="pull-left spacer-right" - > - <span - className="badge badge-success" - > - 8.2 - </span> - </div> - <div> - <PluginChangeLogButton - pluginName="Sonar Foo" - release={ - { - "date": "2020-01-01", - "version": "8.2", - } - } - update={ - { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - { - "filename": "sonar-bar-1.0.jar", - "hash": "hash", - "implementationBuild": "1.0.0.1234", - "key": "sonar-bar", - "name": "Sonar Bar", - "sonarLintSupported": false, - "updatedAt": 100, - "version": "1.0", - }, - ], - "status": "available", - } - } - /> - <p - className="little-spacer-top" - > - <strong> - marketplace.installing_this_plugin_will_also_install_x.Sonar Foo - </strong> - </p> - </div> - </li> - </ul> - </td> - <td - className="text-top width-20" - > - <ul> - <PluginUrls - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - { - "filename": "sonar-bar-1.0.jar", - "hash": "hash", - "implementationBuild": "1.0.0.1234", - "key": "sonar-bar", - "name": "Sonar Bar", - "sonarLintSupported": false, - "updatedAt": 100, - "version": "1.0", - }, - ], - "status": "available", - }, - } - } - /> - <PluginLicense /> - <PluginOrganization - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - { - "filename": "sonar-bar-1.0.jar", - "hash": "hash", - "implementationBuild": "1.0.0.1234", - "key": "sonar-bar", - "name": "Sonar Bar", - "sonarLintSupported": false, - "updatedAt": 100, - "version": "1.0", - }, - ], - "status": "available", - }, - } - } - /> - </ul> - </td> - <PluginStatus - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [ - { - "key": "sonar-foo", - "name": "Sonar Foo", - }, - { - "filename": "sonar-bar-1.0.jar", - "hash": "hash", - "implementationBuild": "1.0.0.1234", - "key": "sonar-bar", - "name": "Sonar Bar", - "sonarLintSupported": false, - "updatedAt": 100, - "version": "1.0", - }, - ], - "status": "available", - }, - } - } - refreshPending={[MockFunction]} - /> -</tr> -`; - -exports[`should render correctly: read only 1`] = ` -<tr> - <PluginDescription - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - /> - <td - className="text-top big-spacer-right" - > - <ul> - <li - className="display-flex-row little-spacer-bottom" - > - <div - className="pull-left spacer-right" - > - <span - className="badge badge-success" - > - 8.2 - </span> - </div> - <div> - <PluginChangeLogButton - pluginName="Sonar Foo" - release={ - { - "date": "2020-01-01", - "version": "8.2", - } - } - update={ - { - "requires": [], - "status": "available", - } - } - /> - </div> - </li> - </ul> - </td> - <td - className="text-top width-20" - > - <ul> - <PluginUrls - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - /> - <PluginLicense /> - <PluginOrganization - plugin={ - { - "key": "sonar-foo", - "name": "Sonar Foo", - "release": { - "date": "2020-01-01", - "version": "8.2", - }, - "update": { - "requires": [], - "status": "available", - }, - } - } - /> - </ul> - </td> -</tr> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginChangeLog-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginChangeLog-test.tsx.snap deleted file mode 100644 index bedb1f82654..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginChangeLog-test.tsx.snap +++ /dev/null @@ -1,105 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="abs-width-300" -> - <h6> - changelog - </h6> - <ul - className="js-plugin-changelog-list" - > - <PluginChangeLogItem - key="0.10" - release={ - { - "changeLogUrl": "https://my.change.log/url", - "date": "2018-06-05", - "description": "Change version description", - "version": "0.10", - } - } - update={ - { - "release": { - "changeLogUrl": "https://my.change.log/url", - "date": "2018-06-05", - "description": "Change version description", - "version": "0.10", - }, - "requires": [], - "status": "COMPATIBLE", - } - } - /> - <PluginChangeLogItem - key="0.11" - release={ - { - "changeLogUrl": "https://my.change.log/url", - "date": "2018-06-08", - "description": "Change version description", - "version": "0.11", - } - } - update={ - { - "release": { - "changeLogUrl": "https://my.change.log/url", - "date": "2018-06-08", - "description": "Change version description", - "version": "0.11", - }, - "requires": [], - "status": "COMPATIBLE", - } - } - /> - <PluginChangeLogItem - release={ - { - "changeLogUrl": "https://my.change.log/url", - "date": "2018-11-05", - "description": "Change version description", - "version": "0.11", - } - } - update={ - { - "previousUpdates": [ - { - "release": { - "changeLogUrl": "https://my.change.log/url", - "date": "2018-06-08", - "description": "Change version description", - "version": "0.11", - }, - "requires": [], - "status": "COMPATIBLE", - }, - { - "release": { - "changeLogUrl": "https://my.change.log/url", - "date": "2018-06-05", - "description": "Change version description", - "version": "0.10", - }, - "requires": [], - "status": "COMPATIBLE", - }, - ], - "requires": [ - { - "description": "Code Analyzer for Java", - "key": "java", - "name": "SonarJava", - }, - ], - "status": "COMPATIBLE", - } - } - /> - </ul> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginDescription-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginDescription-test.tsx.snap deleted file mode 100644 index 3f3bf5627e5..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginDescription-test.tsx.snap +++ /dev/null @@ -1,44 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display the description and category 1`] = ` -<td - className="text-top width-25 big-spacer-right" -> - <div> - <strong - className="js-plugin-name" - > - Foo - </strong> - <span - className="js-plugin-category badge spacer-left" - > - foocategory - </span> - </div> - <div - className="js-plugin-description little-spacer-top" - > - foo description - </div> -</td> -`; - -exports[`should not display any category 1`] = ` -<td - className="text-top width-25 big-spacer-right" -> - <div> - <strong - className="js-plugin-name" - > - Foo - </strong> - </div> - <div - className="js-plugin-description little-spacer-top" - > - foo description - </div> -</td> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginLicense-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginLicense-test.tsx.snap deleted file mode 100644 index 648fe7b2ede..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginLicense-test.tsx.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display the license field 1`] = ` -<Tooltip - overlay="SonarSource license" -> - <li - className="little-spacer-bottom marketplace-plugin-license" - > - <FormattedMessage - defaultMessage="marketplace.licensed_under_x" - id="marketplace.licensed_under_x" - values={ - { - "license": <span - className="js-plugin-license" - > - SonarSource license - </span>, - } - } - /> - </li> -</Tooltip> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginOrganization-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginOrganization-test.tsx.snap deleted file mode 100644 index 055f3c081ac..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginOrganization-test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<li - className="little-spacer-bottom" -> - <FormattedMessage - defaultMessage="marketplace.developed_by_x" - id="marketplace.developed_by_x" - values={ - { - "organization": <a - className="js-plugin-organization" - href="org_url" - rel="noopener noreferrer" - target="_blank" - > - org - </a>, - } - } - /> -</li> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginRiskConsentBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginRiskConsentBox-test.tsx.snap deleted file mode 100644 index 39b0f9ea863..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginRiskConsentBox-test.tsx.snap +++ /dev/null @@ -1,100 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly for community edition 1`] = ` -<div - className="boxed-group it__plugin_risk_consent_box" -> - <h2> - marketplace.risk_consent.title - </h2> - <div - className="boxed-group-inner" - > - <p> - marketplace.risk_consent.description - </p> - <p - className="spacer-top" - > - marketplace.risk_consent.installation - </p> - <Button - className="display-block big-spacer-top button-primary" - onClick={[MockFunction]} - > - marketplace.risk_consent.action - </Button> - </div> -</div> -`; - -exports[`should render correctly for risk consent ACCEPTED 1`] = `""`; - -exports[`should render correctly for risk consent NOT_ACCEPTED 1`] = ` -<div - className="boxed-group it__plugin_risk_consent_box" -> - <h2> - marketplace.risk_consent.title - </h2> - <div - className="boxed-group-inner" - > - <p> - marketplace.risk_consent.description - </p> - <Button - className="display-block big-spacer-top button-primary" - onClick={[MockFunction]} - > - marketplace.risk_consent.action - </Button> - </div> -</div> -`; - -exports[`should render correctly for risk consent REQUIRED 1`] = ` -<div - className="boxed-group it__plugin_risk_consent_box" -> - <h2> - marketplace.risk_consent.title - </h2> - <div - className="boxed-group-inner" - > - <p> - marketplace.risk_consent.description - </p> - <Button - className="display-block big-spacer-top button-primary" - onClick={[MockFunction]} - > - marketplace.risk_consent.action - </Button> - </div> -</div> -`; - -exports[`should render correctly for risk consent undefined 1`] = ` -<div - className="boxed-group it__plugin_risk_consent_box" -> - <h2> - marketplace.risk_consent.title - </h2> - <div - className="boxed-group-inner" - > - <p> - marketplace.risk_consent.description - </p> - <Button - className="display-block big-spacer-top button-primary" - onClick={[MockFunction]} - > - marketplace.risk_consent.action - </Button> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginUrls-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginUrls-test.tsx.snap deleted file mode 100644 index 4a29b84eb51..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginUrls-test.tsx.snap +++ /dev/null @@ -1,53 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display only one url 1`] = ` -<li - className="little-spacer-bottom" -> - <ul - className="list-inline" - > - <li> - <a - className="js-plugin-homepage" - href="homepageurl" - rel="noopener noreferrer" - target="_blank" - > - marketplace.homepage - </a> - </li> - </ul> -</li> -`; - -exports[`should display the urls 1`] = ` -<li - className="little-spacer-bottom" -> - <ul - className="list-inline" - > - <li> - <a - className="js-plugin-homepage" - href="homepageurl" - rel="noopener noreferrer" - target="_blank" - > - marketplace.homepage - </a> - </li> - <li> - <a - className="js-plugin-issues" - href="issuetrackerurl" - rel="noopener noreferrer" - target="_blank" - > - marketplace.issue_tracker - </a> - </li> - </ul> -</li> -`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/utils.ts b/server/sonar-web/src/main/js/apps/marketplace/utils.ts index fbd5ab8fc56..8b93d2ecedb 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/utils.ts +++ b/server/sonar-web/src/main/js/apps/marketplace/utils.ts @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { memoize } from 'lodash'; +import { findLastIndex, memoize } from 'lodash'; +import { getInstalledPlugins, getUpdatesPlugins } from '../../api/plugins'; +import { throwGlobalError } from '../../helpers/error'; import { cleanQuery, parseAsString, serializeString } from '../../helpers/query'; -import { Plugin } from '../../types/plugins'; +import { isDefined } from '../../helpers/types'; +import { InstalledPlugin, Plugin, Update } from '../../types/plugins'; import { RawQuery } from '../../types/types'; export interface Query { @@ -59,3 +62,68 @@ export const serializeQuery = memoize( search: query.search ? serializeString(query.search) : undefined, }) ); + +function getLastUpdates(updates: undefined | Update[]): Update[] { + if (!updates) { + return []; + } + const lastUpdate = ['COMPATIBLE', 'REQUIRES_SYSTEM_UPGRADE', 'DEPS_REQUIRE_SYSTEM_UPGRADE'].map( + (status) => { + const index = findLastIndex(updates, (update) => update.status === status); + return index > -1 ? updates[index] : undefined; + } + ); + return lastUpdate.filter(isDefined); +} + +function addChangelog(update: Update, updates?: Update[]) { + if (!updates) { + return update; + } + const index = updates.indexOf(update); + const previousUpdates = index > 0 ? updates.slice(0, index) : []; + return { ...update, previousUpdates }; +} + +export function getInstalledPluginsWithUpdates(): Promise<InstalledPlugin[]> { + return Promise.all([getInstalledPlugins(), getUpdatesPlugins()]) + .then(([installed, updates]) => + installed.map((plugin: InstalledPlugin) => { + const updatePlugin: InstalledPlugin = updates.plugins.find( + (p: InstalledPlugin) => p.key === plugin.key + ); + if (updatePlugin) { + return { + ...updatePlugin, + ...plugin, + updates: getLastUpdates(updatePlugin.updates).map((update) => + addChangelog(update, updatePlugin.updates) + ), + }; + } + return plugin; + }) + ) + .catch(throwGlobalError); +} + +export function getPluginUpdates(): Promise<InstalledPlugin[]> { + return Promise.all([getUpdatesPlugins(), getInstalledPlugins()]) + .then(([updates, installed]) => + updates.plugins.map((updatePlugin: InstalledPlugin) => { + const updates = getLastUpdates(updatePlugin.updates).map((update) => + addChangelog(update, updatePlugin.updates) + ); + const plugin = installed.find((p: InstalledPlugin) => p.key === updatePlugin.key); + if (plugin) { + return { + ...plugin, + ...updatePlugin, + updates, + }; + } + return { ...updatePlugin, updates }; + }) + ) + .catch(throwGlobalError); +} diff --git a/server/sonar-web/src/main/js/helpers/testSelector.ts b/server/sonar-web/src/main/js/helpers/testSelector.ts index e558c9ce014..832aca63258 100644 --- a/server/sonar-web/src/main/js/helpers/testSelector.ts +++ b/server/sonar-web/src/main/js/helpers/testSelector.ts @@ -112,14 +112,14 @@ class ChainDispatch extends ChainingQuery { container?: HTMLElement, waitForOptions?: waitForOptions ) { - return this.elementQuery.get<T>(await this.insideQuery.find(container, waitForOptions)); + return this.elementQuery.find<T>(await this.insideQuery.find(container, waitForOptions)); } async findAll<T extends HTMLElement = HTMLElement>( container?: HTMLElement, waitForOptions?: waitForOptions ) { - return this.elementQuery.getAll<T>(await this.insideQuery.find(container, waitForOptions)); + return this.elementQuery.findAll<T>(await this.insideQuery.find(container, waitForOptions)); } get<T extends HTMLElement = HTMLElement>(container?: HTMLElement) { @@ -131,11 +131,19 @@ class ChainDispatch extends ChainingQuery { } query<T extends HTMLElement = HTMLElement>(container?: HTMLElement) { - return this.elementQuery.query<T>(this.insideQuery.get(container)); + const innerContainer = this.insideQuery.query(container); + if (innerContainer) { + return this.elementQuery.query<T>(innerContainer); + } + return null; } queryAll<T extends HTMLElement = HTMLElement>(container?: HTMLElement) { - return this.elementQuery.queryAll<T>(this.insideQuery.get(container)); + const innerContainer = this.insideQuery.query(container); + if (innerContainer) { + return this.elementQuery.queryAll<T>(innerContainer); + } + return null; } } |