diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2018-01-31 11:23:12 +0100 |
---|---|---|
committer | Guillaume Jambet <guillaume.jambet@gmail.com> | 2018-03-01 15:21:05 +0100 |
commit | 3c5c062c8bf27f5ed93c72871d9d9ddcf4c0c548 (patch) | |
tree | 0efb13547233d7d159a6732add6aa4d850349357 /server/sonar-web/src/main | |
parent | ee3a17fa0f5a597f7f8d76562cb8d15fc6b3b342 (diff) | |
download | sonarqube-3c5c062c8bf27f5ed93c72871d9d9ddcf4c0c548.tar.gz sonarqube-3c5c062c8bf27f5ed93c72871d9d9ddcf4c0c548.zip |
SONAR-10344 Create the webhooks console page
* SONAR-10348 Create the webhooks console page
* SONAR-10349 Add webhook console at global admin scope
* SONAR-10349 Add webhook console at project scope
* SONAR-10349 Add webhook console at organization scope
Diffstat (limited to 'server/sonar-web/src/main')
23 files changed, 731 insertions, 17 deletions
diff --git a/server/sonar-web/src/main/js/api/webhooks.ts b/server/sonar-web/src/main/js/api/webhooks.ts new file mode 100644 index 00000000000..fa526855e02 --- /dev/null +++ b/server/sonar-web/src/main/js/api/webhooks.ts @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 { getJSON } from '../helpers/request'; +import throwGlobalError from '../app/utils/throwGlobalError'; + +export interface Delivery { + id: string; + at: string; + success: boolean; + httpStatus: number; + durationMs: number; +} + +export interface Webhook { + key: string; + name: string; + url: string; + latestDelivery?: Delivery; +} + +export function searchWebhooks(data: { + organization: string | undefined; + project?: string; +}): Promise<{ webhooks: Webhook[] }> { + return getJSON('/api/webhooks/search', data).catch(throwGlobalError); +} diff --git a/server/sonar-web/src/main/js/app/components/ProjectAdminContainer.js b/server/sonar-web/src/main/js/app/components/ProjectAdminContainer.tsx index 2c70fb9401d..612f4bb043f 100644 --- a/server/sonar-web/src/main/js/app/components/ProjectAdminContainer.js +++ b/server/sonar-web/src/main/js/app/components/ProjectAdminContainer.tsx @@ -17,20 +17,22 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; +import * as React from 'react'; import handleRequiredAuthorization from '../utils/handleRequiredAuthorization'; +import { Component, Branch } from '../types'; -export default class ProjectAdminContainer extends React.PureComponent { - /*:: - props: { - component: { - configuration?: { - showSettings: boolean - } - } - }; - */ +interface Props { + children: JSX.Element; + branch?: Branch; + branches: Branch[]; + component: Component; + isInProgress?: boolean; + isPending?: boolean; + onBranchesChange: () => void; + onComponentChange: (changes: {}) => void; +} +export default class ProjectAdminContainer extends React.PureComponent<Props> { componentDidMount() { this.checkPermissions(); } @@ -39,17 +41,17 @@ export default class ProjectAdminContainer extends React.PureComponent { this.checkPermissions(); } - isProjectAdmin() { - const { configuration } = this.props.component; - return configuration != null && configuration.showSettings; - } - checkPermissions() { if (!this.isProjectAdmin()) { handleRequiredAuthorization(); } } + isProjectAdmin() { + const { configuration } = this.props.component; + return configuration != null && configuration.showSettings; + } + render() { if (!this.isProjectAdmin()) { return null; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx index 564a54ea318..bb56f7751a8 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx @@ -243,6 +243,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> { this.renderPermissionsLink(), this.renderBackgroundTasksLink(), this.renderUpdateKeyLink(), + this.renderWebhooksLink(), ...this.renderAdminExtensions(), this.renderDeletionLink() ]; @@ -394,6 +395,21 @@ export default class ComponentNavMenu extends React.PureComponent<Props> { ); } + renderWebhooksLink() { + if (!this.getConfiguration().showSettings || !this.isProject()) { + return null; + } + return ( + <li key="webhooks"> + <Link + to={{ pathname: '/project/webhooks', query: { id: this.props.component.key } }} + activeClassName="active"> + {translate('webhooks.page')} + </Link> + </li> + ); + } + renderDeletionLink() { const { qualifier } = this.props.component; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap index 56558732e74..592b1d44eee 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap @@ -151,6 +151,25 @@ exports[`should work for all qualifiers 1`] = ` </Link> </li> <li + key="webhooks" + > + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/project/webhooks", + "query": Object { + "id": "foo", + }, + } + } + > + webhooks.page + </Link> + </li> + <li key="project_delete" > <Link @@ -1061,6 +1080,25 @@ exports[`should work with extensions 1`] = ` </Link> </li> <li + key="webhooks" + > + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/project/webhooks", + "query": Object { + "id": "foo", + }, + } + } + > + webhooks.page + </Link> + </li> + <li key="foo" > <Link @@ -1293,6 +1331,25 @@ exports[`should work with multiple extensions 1`] = ` </Link> </li> <li + key="webhooks" + > + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/project/webhooks", + "query": Object { + "id": "foo", + }, + } + } + > + webhooks.page + </Link> + </li> + <li key="foo" > <Link diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx index 32433c78d15..7f50c588689 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx @@ -117,6 +117,11 @@ export default class SettingsNav extends React.PureComponent<Props> { {translate('custom_metrics.page')} </IndexLink> </li> + <li> + <IndexLink to="/admin/webhooks" activeClassName="active"> + {translate('webhooks.page')} + </IndexLink> + </li> {extensionsWithoutSupport.map(this.renderExtension)} </ul> </li> diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap index 64822b179f6..a80573d7d9d 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap @@ -55,6 +55,14 @@ exports[`should work with extensions 1`] = ` custom_metrics.page </IndexLink> </li> + <li> + <IndexLink + activeClassName="active" + to="/admin/webhooks" + > + webhooks.page + </IndexLink> + </li> <li key="foo" > diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js index c6af220da94..98968f52f68 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.js +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js @@ -74,6 +74,7 @@ import settingsRoutes from '../../apps/settings/routes'; import systemRoutes from '../../apps/system/routes'; import usersRoutes from '../../apps/users/routes'; import webAPIRoutes from '../../apps/web-api/routes'; +import webhooksRoutes from '../../apps/webhooks/routes'; import { maintenanceRoutes, setupRoutes } from '../../apps/maintenance/routes'; import { globalPermissionsRoutes, projectPermissionsRoutes } from '../../apps/permissions/routes'; @@ -212,6 +213,7 @@ const startReactApp = () => { <Route path="project/branches" childRoutes={projectBranchesRoutes} /> <Route path="project/settings" childRoutes={settingsRoutes} /> <Route path="project_roles" childRoutes={projectPermissionsRoutes} /> + <Route path="project/webhooks" childRoutes={webhooksRoutes} /> </Route> {projectAdminRoutes} </Route> @@ -232,6 +234,7 @@ const startReactApp = () => { <Route path="system" childRoutes={systemRoutes} /> <Route path="marketplace" childRoutes={marketplaceRoutes} /> <Route path="users" childRoutes={usersRoutes} /> + <Route path="webhooks" childRoutes={webhooksRoutes} /> </Route> </Route> diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationAdministration.tsx b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationAdministration.tsx index 0446e83c50d..cc2b9631ee8 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationAdministration.tsx +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationAdministration.tsx @@ -93,6 +93,11 @@ export default function OrganizationNavigationAdministration({ location, organiz </Link> </li> <li> + <Link to={`/organizations/${organization.key}/webhooks`} activeClassName="active"> + {translate('webhooks.page')} + </Link> + </li> + <li> <Link to={`/organizations/${organization.key}/edit`} activeClassName="active"> {translate('edit')} </Link> diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationAdministration-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationAdministration-test.tsx.snap index 713261b2290..c7764bf1c8b 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationAdministration-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationAdministration-test.tsx.snap @@ -63,6 +63,16 @@ exports[`renders 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} + to="/organizations/foo/webhooks" + > + webhooks.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} to="/organizations/foo/edit" > edit diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.ts b/server/sonar-web/src/main/js/apps/organizations/routes.ts index 2b11756dbc3..3277341cfb3 100644 --- a/server/sonar-web/src/main/js/apps/organizations/routes.ts +++ b/server/sonar-web/src/main/js/apps/organizations/routes.ts @@ -31,6 +31,7 @@ import ProjectManagementApp from '../projectsManagement/AppContainer'; import codingRulesRoutes from '../coding-rules/routes'; import qualityGatesRoutes from '../quality-gates/routes'; import qualityProfilesRoutes from '../quality-profiles/routes'; +import webhooksRoutes from '../webhooks/routes'; import Issues from '../issues/components/AppContainer'; import GroupsApp from '../groups/components/App'; import OrganizationPageExtension from '../../app/components/extensions/OrganizationPageExtension'; @@ -88,7 +89,8 @@ const routes = [ { path: 'groups', component: GroupsApp }, { path: 'permissions', component: GlobalPermissionsApp }, { path: 'permission_templates', component: PermissionTemplateApp }, - { path: 'projects_management', component: ProjectManagementApp } + { path: 'projects_management', component: ProjectManagementApp }, + { path: 'webhooks', childRoutes: webhooksRoutes } ] } ] diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/App.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/App.tsx new file mode 100644 index 00000000000..36f26f55913 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/App.tsx @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { Helmet } from 'react-helmet'; +import PageHeader from './PageHeader'; +import WebhooksList from './WebhooksList'; +import { searchWebhooks, Webhook } from '../../../api/webhooks'; +import { LightComponent, Organization } from '../../../app/types'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + organization: Organization | undefined; + component?: LightComponent; +} + +interface State { + loading: boolean; + webhooks: Webhook[]; +} + +export default class App extends React.PureComponent<Props, State> { + mounted: boolean = false; + state: State = { loading: true, webhooks: [] }; + + componentDidMount() { + this.mounted = true; + this.fetchWebhooks(); + } + + componentWillUnmount() { + this.mounted = false; + } + + fetchWebhooks = ({ organization, component } = this.props) => { + this.setState({ loading: true }); + searchWebhooks({ + organization: organization && organization.key, + project: component && component.key + }).then( + ({ webhooks }) => { + if (this.mounted) { + this.setState({ loading: false, webhooks }); + } + }, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); + }; + + render() { + const { loading, webhooks } = this.state; + return ( + <div className="page page-limited"> + <Helmet title={translate('webhooks.page')} /> + <PageHeader loading={loading} /> + {!loading && ( + <div className="boxed-group boxed-group-inner"> + <WebhooksList webhooks={webhooks} /> + </div> + )} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/PageHeader.tsx new file mode 100644 index 00000000000..229b835e5e9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/PageHeader.tsx @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + loading: boolean; +} + +export default function PageHeader({ loading }: Props) { + return ( + <header className="page-header"> + <h1 className="page-title">{translate('webhooks.page')}</h1> + {loading && <i className="spinner" />} + + <p className="page-description"> + <FormattedMessage + defaultMessage={translate('webhooks.description')} + id={'webhooks.description'} + values={{ + url: ( + <a + href="https://redirect.sonarsource.com/doc/webhooks.html" + rel="noopener noreferrer" + target="_blank"> + {translate('webhooks.documentation_link')} + </a> + ) + }} + /> + </p> + </header> + ); +} diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx new file mode 100644 index 00000000000..1be6a9da181 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { Webhook } from '../../../api/webhooks'; + +interface Props { + webhook: Webhook; +} + +export default function WebhookItem({ webhook }: Props) { + return ( + <tr> + <td>{webhook.name}</td> + <td>{webhook.url}</td> + </tr> + ); +} diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx new file mode 100644 index 00000000000..61689e75561 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import WebhookItem from './WebhookItem'; +import { Webhook } from '../../../api/webhooks'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + webhooks: Webhook[]; +} + +export default class WebhooksList extends React.PureComponent<Props> { + renderHeader = () => ( + <thead> + <tr> + <th>{translate('name')}</th> + <th>{translate('webhooks.url')}</th> + </tr> + </thead> + ); + + renderNoWebhooks = () => ( + <tr> + <td>{translate('webhooks.no_result')}</td> + </tr> + ); + + render() { + const { webhooks } = this.props; + return ( + <table className="data zebra"> + {this.renderHeader()} + <tbody> + {webhooks.length > 0 + ? webhooks.map(webhook => <WebhookItem key={webhook.key} webhook={webhook} />) + : this.renderNoWebhooks()} + </tbody> + </table> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/App-test.tsx new file mode 100644 index 00000000000..085def84f20 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/App-test.tsx @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import App from '../App'; +import { searchWebhooks } from '../../../../api/webhooks'; +import { Visibility } from '../../../../app/types'; + +jest.mock('../../../../api/webhooks', () => ({ + searchWebhooks: jest.fn(() => Promise.resolve({ webhooks: [] })) +})); + +const organization = { key: 'foo', name: 'Foo', projectVisibility: Visibility.Private }; +const component = { key: 'bar', organization: 'foo', qualifier: 'TRK' }; + +beforeEach(() => { + (searchWebhooks as jest.Mock<any>).mockClear(); +}); + +it('should be in loading status', () => { + expect(shallow(<App organization={undefined} />)).toMatchSnapshot(); +}); + +it('should fetch webhooks and display them', async () => { + const wrapper = shallow(<App organization={organization} />); + expect(wrapper.state('loading')).toBeTruthy(); + + await new Promise(setImmediate); + expect(searchWebhooks).toHaveBeenCalledWith({ organization: organization.key }); + + wrapper.update(); + expect(wrapper).toMatchSnapshot(); +}); + +describe('should correctly fetch webhooks when', () => { + it('on global scope', async () => { + shallow(<App organization={undefined} />); + + await new Promise(setImmediate); + expect(searchWebhooks).toHaveBeenCalledWith({ organization: undefined }); + }); + + it('on project scope', async () => { + shallow(<App organization={undefined} component={component} />); + + await new Promise(setImmediate); + expect(searchWebhooks).toHaveBeenCalledWith({ project: component.key }); + }); + + it('on organization scope', async () => { + shallow(<App organization={organization} component={undefined} />); + + await new Promise(setImmediate); + expect(searchWebhooks).toHaveBeenCalledWith({ organization: organization.key }); + }); + + it('on project scope within an organization', async () => { + shallow(<App organization={organization} component={component} />); + + await new Promise(setImmediate); + expect(searchWebhooks).toHaveBeenCalledWith({ + organization: organization.key, + project: component.key + }); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/PageHeader-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/PageHeader-test.tsx new file mode 100644 index 00000000000..148d1570cd4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/PageHeader-test.tsx @@ -0,0 +1,26 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import PageHeader from '../PageHeader'; + +it('should render correctly', () => { + expect(shallow(<PageHeader loading={true} />)).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx new file mode 100644 index 00000000000..ab0a1a5d0e4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhookItem-test.tsx @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import WebhookItem from '../WebhookItem'; + +const webhook = { key: '1', name: 'my webhook', url: 'http://webhook.target' }; + +it('should render correctly', () => { + expect(shallow(<WebhookItem webhook={webhook} />)).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhooksList-test.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhooksList-test.tsx new file mode 100644 index 00000000000..e0337cc8f5c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/WebhooksList-test.tsx @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import WebhooksList from '../WebhooksList'; + +const webhooks = [ + { key: '1', name: 'my webhook', url: 'http://webhook.target' }, + { key: '2', name: 'jenkins webhook', url: 'http://jenkins.target' } +]; + +it('should correctly render empty webhook list', () => { + expect(shallow(<WebhooksList webhooks={[]} />)).toMatchSnapshot(); +}); + +it('should correctly render the webhooks', () => { + expect(shallow(<WebhooksList webhooks={webhooks} />)).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/App-test.tsx.snap new file mode 100644 index 00000000000..717e2e76bb0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/App-test.tsx.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should be in loading status 1`] = ` +<div + className="page page-limited" +> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="webhooks.page" + /> + <PageHeader + loading={true} + /> +</div> +`; + +exports[`should fetch webhooks and display them 1`] = ` +<div + className="page page-limited" +> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="webhooks.page" + /> + <PageHeader + loading={false} + /> + <div + className="boxed-group boxed-group-inner" + > + <WebhooksList + webhooks={Array []} + /> + </div> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/PageHeader-test.tsx.snap new file mode 100644 index 00000000000..e40da7ba2d3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/PageHeader-test.tsx.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<header + className="page-header" +> + <h1 + className="page-title" + > + webhooks.page + </h1> + <i + className="spinner" + /> + <p + className="page-description" + > + <FormattedMessage + defaultMessage="webhooks.description" + id="webhooks.description" + values={ + Object { + "url": <a + href="https://redirect.sonarsource.com/doc/webhooks.html" + rel="noopener noreferrer" + target="_blank" + > + webhooks.documentation_link + </a>, + } + } + /> + </p> +</header> +`; diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap new file mode 100644 index 00000000000..b6968c76bed --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<tr> + <td> + my webhook + </td> + <td> + http://webhook.target + </td> +</tr> +`; diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhooksList-test.tsx.snap b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhooksList-test.tsx.snap new file mode 100644 index 00000000000..01bcb34685a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhooksList-test.tsx.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should correctly render empty webhook list 1`] = ` +<p> + webhooks.no_result +</p> +`; + +exports[`should correctly render the webhooks 1`] = ` +<table + className="data zebra" +> + <thead> + <tr> + <th> + name + </th> + <th> + webhooks.url + </th> + </tr> + </thead> + <tbody> + <WebhookItem + key="1" + webhook={ + Object { + "key": "1", + "name": "my webhook", + "url": "http://webhook.target", + } + } + /> + <WebhookItem + key="2" + webhook={ + Object { + "key": "2", + "name": "jenkins webhook", + "url": "http://jenkins.target", + } + } + /> + </tbody> +</table> +`; diff --git a/server/sonar-web/src/main/js/apps/webhooks/routes.ts b/server/sonar-web/src/main/js/apps/webhooks/routes.ts new file mode 100644 index 00000000000..fbd6fae2b97 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/webhooks/routes.ts @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 { RouterState, RouteComponent } from 'react-router'; + +const routes = [ + { + indexRoute: { + getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { + import('./components/App').then(i => callback(null, i.default)); + } + } + } +]; + +export default routes; |