From 238191f069e82f40e0e3a89d7914481975bcb200 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Wed, 22 Aug 2018 13:33:48 +0200 Subject: [PATCH] rewrite project deletion in typescript (#633) --- .../src/main/js/app/utils/startReactApp.js | 4 +- .../js/apps/project-admin/deletion/Form.js | 108 ------------------ .../Deletion.js => projectDeletion/App.tsx} | 11 +- .../src/main/js/apps/projectDeletion/Form.tsx | 65 +++++++++++ .../Header.js => projectDeletion/Header.tsx} | 25 ++-- .../projectDeletion/__tests__/App-test.tsx | 28 +++++ .../projectDeletion/__tests__/Form-test.tsx | 74 ++++++++++++ .../projectDeletion/__tests__/Header-test.tsx | 28 +++++ .../__tests__/__snapshots__/App-test.tsx.snap | 31 +++++ .../__snapshots__/Form-test.tsx.snap | 21 ++++ .../__snapshots__/Header-test.tsx.snap | 52 +++++++++ 11 files changed, 324 insertions(+), 123 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js rename server/sonar-web/src/main/js/apps/{project-admin/deletion/Deletion.js => projectDeletion/App.tsx} (82%) create mode 100644 server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx rename server/sonar-web/src/main/js/apps/{project-admin/deletion/Header.js => projectDeletion/Header.tsx} (65%) create mode 100644 server/sonar-web/src/main/js/apps/projectDeletion/__tests__/App-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Header-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/App-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Form-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Header-test.tsx.snap 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 9b27ccbbb23..d521a298da7 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.js +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js @@ -218,9 +218,7 @@ const startReactApp = (lang, currentUser, appState) => { - import('../../apps/project-admin/deletion/Deletion') - )} + component={lazyLoad(() => import('../../apps/projectDeletion/App'))} /> { - this.setState({ modalOpen: true }); - }; - - closeModal = () => this.setState({ modalOpen: false }); - - stopLoading = () => { - if (this.mounted) { - this.setState({ loading: false }); - } - }; - - handleSubmit = event => { - event.preventDefault(); - this.setState({ loading: true }); - const { component } = this.props; - const deleteMethod = component.qualifier === 'TRK' ? deleteProject : deletePortfolio; - deleteMethod(component.key) - .then(() => this.context.router.replace(component.qualifier === 'TRK' ? '/' : '/portfolios')) - .catch(this.stopLoading); - }; - - render() { - const { component } = this.props; - - return ( -
- - - {this.state.modalOpen && ( - -
-
-

{translate('qualifier.delete.TRK')}

-
-
-
- {translateWithParameters( - 'project_deletion.delete_resource_confirmation', - component.name - )} -
-
- {this.state.loading && } - - {translate('delete')} - - - {translate('cancel')} - -
- - - )} -
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js b/server/sonar-web/src/main/js/apps/projectDeletion/App.tsx similarity index 82% rename from server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js rename to server/sonar-web/src/main/js/apps/projectDeletion/App.tsx index 887ebac7f48..87413a22988 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js +++ b/server/sonar-web/src/main/js/apps/projectDeletion/App.tsx @@ -17,13 +17,18 @@ * 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 Helmet from 'react-helmet'; import Header from './Header'; import Form from './Form'; -import { translate } from '../../../helpers/l10n'; +import { Component } from '../../app/types'; +import { translate } from '../../helpers/l10n'; -export default function Deletion(props) { +interface Props { + component: Pick; +} + +export default function App(props: Props) { return (
diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx new file mode 100644 index 00000000000..1114935a92f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx @@ -0,0 +1,65 @@ +/* + * 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 { withRouter, WithRouterProps } from 'react-router'; +import { deleteProject, deletePortfolio } from '../../api/components'; +import { Button } from '../../components/ui/buttons'; +import { translate, translateWithParameters } from '../../helpers/l10n'; +import { Component } from '../../app/types'; +import ConfirmButton from '../../components/controls/ConfirmButton'; + +interface Props { + component: Pick; +} + +export default withRouter( + class Form extends React.PureComponent { + handleDelete = () => { + const { component } = this.props; + const isProject = component.qualifier === 'TRK'; + const deleteMethod = isProject ? deleteProject : deletePortfolio; + const redirectTo = isProject ? '/' : '/portfolios'; + return deleteMethod(component.key).then(() => { + this.props.router.replace(redirectTo); + }); + }; + + render() { + const { component } = this.props; + return ( + + {({ onClick }) => ( + + )} + + ); + } + } +); diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Header.js b/server/sonar-web/src/main/js/apps/projectDeletion/Header.tsx similarity index 65% rename from server/sonar-web/src/main/js/apps/project-admin/deletion/Header.js rename to server/sonar-web/src/main/js/apps/projectDeletion/Header.tsx index a82aa398834..4cd39cec859 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/deletion/Header.js +++ b/server/sonar-web/src/main/js/apps/projectDeletion/Header.tsx @@ -17,17 +17,24 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import { translate } from '../../../helpers/l10n'; +import * as React from 'react'; +import { translate } from '../../helpers/l10n'; +import { Component } from '../../app/types'; -export default function Header(props /*: { component: { qualifier: string } } */) { +interface Props { + component: Pick; +} + +export default function Header(props: Props) { const { qualifier } = props.component; - const description = ['VW', 'SVW'].includes(qualifier) - ? translate('portfolio_deletion.page.description') - : qualifier === 'APP' - ? translate('application_deletion.page.description') - : translate('project_deletion.page.description'); + let description: string; + if (['VW', 'SVW'].includes(qualifier)) { + description = translate('portfolio_deletion.page.description'); + } else if (qualifier === 'APP') { + description = translate('application_deletion.page.description'); + } else { + description = translate('project_deletion.page.description'); + } return (
diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/App-test.tsx new file mode 100644 index 00000000000..5b58e4557ff --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/App-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 App from '../App'; + +it('should render', () => { + expect( + shallow() + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx new file mode 100644 index 00000000000..75f013ad637 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Form-test.tsx @@ -0,0 +1,74 @@ +/* + * 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 Form from '../Form'; +import { deleteProject, deletePortfolio } from '../../../api/components'; + +jest.mock('../../../api/components', () => ({ + deleteProject: jest.fn().mockResolvedValue(undefined), + deletePortfolio: jest.fn().mockResolvedValue(undefined) +})); + +beforeEach(() => { + (deleteProject as jest.Mock).mockClear(); + (deletePortfolio as jest.Mock).mockClear(); +}); + +it('should render', () => { + const component = { key: 'foo', name: 'Foo', qualifier: 'TRK' }; + const form = shallow(
).dive(); + expect(form).toMatchSnapshot(); + expect(form.prop('children')({ onClick: jest.fn() })).toMatchSnapshot(); +}); + +it('should delete project', async () => { + const component = { key: 'foo', name: 'Foo', qualifier: 'TRK' }; + const router = getMockedRouter(); + const form = shallow().dive(); + form.prop('onConfirm')(); + expect(deleteProject).toBeCalledWith('foo'); + await new Promise(setImmediate); + expect(router.replace).toBeCalledWith('/'); +}); + +it('should delete portfolio', async () => { + const component = { key: 'foo', name: 'Foo', qualifier: 'VW' }; + const router = getMockedRouter(); + const form = shallow().dive(); + form.prop('onConfirm')(); + expect(deletePortfolio).toBeCalledWith('foo'); + expect(deleteProject).not.toBeCalled(); + await new Promise(setImmediate); + expect(router.replace).toBeCalledWith('/portfolios'); +}); + +// have to mock all properties to pass the prop types check +const getMockedRouter = () => ({ + createHref: jest.fn(), + createPath: jest.fn(), + go: jest.fn(), + goBack: jest.fn(), + goForward: jest.fn(), + isActive: jest.fn(), + push: jest.fn(), + replace: jest.fn(), + setRouteLeaveHook: jest.fn() +}); diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Header-test.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Header-test.tsx new file mode 100644 index 00000000000..fc006b4f35d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/Header-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 Header from '../Header'; + +it('should render', () => { + expect(shallow(
)).toMatchSnapshot(); + expect(shallow(
)).toMatchSnapshot(); + expect(shallow(
)).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/App-test.tsx.snap new file mode 100644 index 00000000000..4bbc7bf918a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/App-test.tsx.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render 1`] = ` +
+ +
+ +
+`; diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Form-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Form-test.tsx.snap new file mode 100644 index 00000000000..b548aaf7e7f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Form-test.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render 1`] = ` + +`; + +exports[`should render 2`] = ` + +`; diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Header-test.tsx.snap new file mode 100644 index 00000000000..9b06736013e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectDeletion/__tests__/__snapshots__/Header-test.tsx.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render 1`] = ` +
+

+ deletion.page +

+
+ project_deletion.page.description +
+
+`; + +exports[`should render 2`] = ` +
+

+ deletion.page +

+
+ portfolio_deletion.page.description +
+
+`; + +exports[`should render 3`] = ` +
+

+ deletion.page +

+
+ application_deletion.page.description +
+
+`; -- 2.39.5