@@ -17,18 +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 { sortBy } from 'lodash'; | |||
import * as React from 'react'; | |||
import { | |||
InstalledPlugin, | |||
isAvailablePlugin, | |||
isInstalledPlugin, | |||
PendingPlugin, | |||
Plugin | |||
} from '../../types/plugins'; | |||
import { isAvailablePlugin, isInstalledPlugin, PendingPlugin, Plugin } from '../../types/plugins'; | |||
import PluginAvailable from './components/PluginAvailable'; | |||
import PluginInstalled from './components/PluginInstalled'; | |||
interface Props { | |||
export interface PluginsListProps { | |||
plugins: Plugin[]; | |||
pending: { | |||
installing: PendingPlugin[]; | |||
@@ -39,61 +34,53 @@ interface Props { | |||
refreshPending: () => void; | |||
} | |||
export default class PluginsList extends React.PureComponent<Props> { | |||
getPluginStatus = (plugin: Plugin): string | undefined => { | |||
const { installing, updating, removing } = this.props.pending; | |||
if (installing.find(p => p.key === plugin.key)) { | |||
return 'installing'; | |||
} | |||
if (updating.find(p => p.key === plugin.key)) { | |||
return 'updating'; | |||
} | |||
if (removing.find(p => p.key === plugin.key)) { | |||
return 'removing'; | |||
} | |||
return undefined; | |||
}; | |||
function getPluginStatus(plugin: Plugin, pending: PluginsListProps['pending']): string | undefined { | |||
const { installing, updating, removing } = pending; | |||
if (installing.find(p => p.key === plugin.key)) { | |||
return 'installing'; | |||
} | |||
if (updating.find(p => p.key === plugin.key)) { | |||
return 'updating'; | |||
} | |||
if (removing.find(p => p.key === plugin.key)) { | |||
return 'removing'; | |||
} | |||
return undefined; | |||
} | |||
renderPlugin = (plugin: Plugin, installedPlugins: InstalledPlugin[]) => { | |||
const status = this.getPluginStatus(plugin); | |||
if (isInstalledPlugin(plugin)) { | |||
return ( | |||
<PluginInstalled | |||
plugin={plugin} | |||
readOnly={this.props.readOnly} | |||
refreshPending={this.props.refreshPending} | |||
status={status} | |||
/> | |||
); | |||
} | |||
if (isAvailablePlugin(plugin)) { | |||
return ( | |||
<PluginAvailable | |||
installedPlugins={installedPlugins} | |||
plugin={plugin} | |||
readOnly={this.props.readOnly} | |||
refreshPending={this.props.refreshPending} | |||
status={status} | |||
/> | |||
); | |||
} | |||
return null; | |||
}; | |||
export default function PluginsList(props: PluginsListProps) { | |||
const { pending, plugins, readOnly } = props; | |||
const installedPlugins = plugins.filter(isInstalledPlugin); | |||
return ( | |||
<div className="boxed-group boxed-group-inner" id="marketplace-plugins"> | |||
<ul> | |||
{sortBy(plugins, ({ name }) => name).map(plugin => ( | |||
<li className="panel panel-vertical" key={plugin.key}> | |||
<table className="marketplace-plugin-table"> | |||
<tbody> | |||
{isInstalledPlugin(plugin) && ( | |||
<PluginInstalled | |||
plugin={plugin} | |||
readOnly={readOnly} | |||
refreshPending={props.refreshPending} | |||
status={getPluginStatus(plugin, pending)} | |||
/> | |||
)} | |||
render() { | |||
const installedPlugins = this.props.plugins.filter(isInstalledPlugin); | |||
return ( | |||
<div className="boxed-group boxed-group-inner" id="marketplace-plugins"> | |||
<ul> | |||
{this.props.plugins.map(plugin => ( | |||
<li className="panel panel-vertical" key={plugin.key}> | |||
<table className="marketplace-plugin-table"> | |||
<tbody>{this.renderPlugin(plugin, installedPlugins)}</tbody> | |||
</table> | |||
</li> | |||
))} | |||
</ul> | |||
</div> | |||
); | |||
} | |||
{isAvailablePlugin(plugin) && ( | |||
<PluginAvailable | |||
installedPlugins={installedPlugins} | |||
plugin={plugin} | |||
readOnly={readOnly} | |||
refreshPending={props.refreshPending} | |||
status={getPluginStatus(plugin, pending)} | |||
/> | |||
)} | |||
</tbody> | |||
</table> | |||
</li> | |||
))} | |||
</ul> | |||
</div> | |||
); | |||
} |
@@ -0,0 +1,53 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 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} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,204 @@ | |||
// 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={Array []} | |||
plugin={ | |||
Object { | |||
"key": "sonar-bar", | |||
"name": "Sonar Bar", | |||
"release": Object { | |||
"date": "2020-01-01", | |||
"version": "8.2", | |||
}, | |||
"update": Object { | |||
"requires": Array [], | |||
"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={Array []} | |||
plugin={ | |||
Object { | |||
"key": "sonar-baz", | |||
"name": "Sonar Baz", | |||
"release": Object { | |||
"date": "2020-01-01", | |||
"version": "8.2", | |||
}, | |||
"update": Object { | |||
"requires": Array [], | |||
"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={Array []} | |||
plugin={ | |||
Object { | |||
"key": "sonar-foo", | |||
"name": "Sonar Foo", | |||
"release": Object { | |||
"date": "2020-01-01", | |||
"version": "8.2", | |||
}, | |||
"update": Object { | |||
"requires": Array [], | |||
"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={Array []} | |||
plugin={ | |||
Object { | |||
"key": "sonar-bar", | |||
"name": "Sonar Bar", | |||
"release": Object { | |||
"date": "2020-01-01", | |||
"version": "8.2", | |||
}, | |||
"update": Object { | |||
"requires": Array [], | |||
"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={Array []} | |||
plugin={ | |||
Object { | |||
"key": "sonar-baz", | |||
"name": "Sonar Baz", | |||
"release": Object { | |||
"date": "2020-01-01", | |||
"version": "8.2", | |||
}, | |||
"update": Object { | |||
"requires": Array [], | |||
"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={Array []} | |||
plugin={ | |||
Object { | |||
"key": "sonar-foo", | |||
"name": "Sonar Foo", | |||
"release": Object { | |||
"date": "2020-01-01", | |||
"version": "8.2", | |||
}, | |||
"update": Object { | |||
"requires": Array [], | |||
"status": "available", | |||
}, | |||
} | |||
} | |||
readOnly={false} | |||
refreshPending={[MockFunction]} | |||
status="installing" | |||
/> | |||
</tbody> | |||
</table> | |||
</li> | |||
</ul> | |||
</div> | |||
`; |
@@ -18,7 +18,14 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { AvailablePlugin, InstalledPlugin, Plugin, Release, Update } from '../../types/plugins'; | |||
import { | |||
AvailablePlugin, | |||
InstalledPlugin, | |||
PendingPlugin, | |||
Plugin, | |||
Release, | |||
Update | |||
} from '../../types/plugins'; | |||
export function mockPlugin(overrides: Partial<Plugin> = {}): Plugin { | |||
return { | |||
@@ -28,6 +35,16 @@ export function mockPlugin(overrides: Partial<Plugin> = {}): Plugin { | |||
}; | |||
} | |||
export function mockPendingPlugin(overrides: Partial<PendingPlugin> = {}): PendingPlugin { | |||
return { | |||
key: 'sonar-foo', | |||
name: 'Sonar Foo', | |||
version: '1.0', | |||
implementationBuild: '1.0.0.1234', | |||
...overrides | |||
}; | |||
} | |||
export function mockInstalledPlugin(overrides: Partial<InstalledPlugin> = {}): InstalledPlugin { | |||
return { | |||
key: 'sonar-bar', |