diff options
author | stanislavh <stanislav.honcharov@sonarsource.com> | 2023-04-13 14:10:07 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-04-14 20:02:47 +0000 |
commit | 2768a0aefc190bfa923110a1371c5eeef223ebb0 (patch) | |
tree | 8f9999030b1be7d0d997aa551080d9c5d403e6b2 /server/sonar-web/src | |
parent | db8375a594fd1da6cabae829169fcbb177495432 (diff) | |
download | sonarqube-2768a0aefc190bfa923110a1371c5eeef223ebb0.tar.gz sonarqube-2768a0aefc190bfa923110a1371c5eeef223ebb0.zip |
SONAR-18435 Migrate Project Key to RTl
Diffstat (limited to 'server/sonar-web/src')
-rw-r--r-- | server/sonar-web/src/main/js/api/mocks/ComponentsServiceMock.ts | 29 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/projectKey/ProjectKeyApp.tsx (renamed from server/sonar-web/src/main/js/apps/projectKey/Key.tsx) | 35 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/projectKey/__tests__/Key-test.tsx | 44 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/projectKey/__tests__/ProjectKeyApp-it.tsx | 95 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/projectKey/__tests__/UpdateForm-test.tsx | 91 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/Key-test.tsx.snap | 54 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/UpdateForm-test.tsx.snap | 71 |
7 files changed, 131 insertions, 288 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/ComponentsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/ComponentsServiceMock.ts index ba345fe6d08..359292c3f7e 100644 --- a/server/sonar-web/src/main/js/api/mocks/ComponentsServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/ComponentsServiceMock.ts @@ -42,6 +42,7 @@ import { SourceViewerFile, } from '../../types/types'; import { + changeKey, getChildren, getComponentData, getComponentForSourceViewer, @@ -387,15 +388,16 @@ export default class ComponentsServiceMock { this.components = cloneDeep(this.defaultComponents); this.sourceFiles = cloneDeep(this.defaultSourceFiles); - (getComponentTree as jest.Mock).mockImplementation(this.handleGetComponentTree); - (getChildren as jest.Mock).mockImplementation(this.handleGetChildren); - (getTree as jest.Mock).mockImplementation(this.handleGetTree); - (getComponentData as jest.Mock).mockImplementation(this.handleGetComponentData); - (getComponentForSourceViewer as jest.Mock).mockImplementation( - this.handleGetComponentForSourceViewer - ); - (getDuplications as jest.Mock).mockImplementation(this.handleGetDuplications); - (getSources as jest.Mock).mockImplementation(this.handleGetSources); + jest.mocked(getComponentTree).mockImplementation(this.handleGetComponentTree); + jest.mocked(getChildren).mockImplementation(this.handleGetChildren); + jest.mocked(getTree).mockImplementation(this.handleGetTree); + jest.mocked(getComponentData).mockImplementation(this.handleGetComponentData); + jest + .mocked(getComponentForSourceViewer) + .mockImplementation(this.handleGetComponentForSourceViewer); + jest.mocked(getDuplications).mockImplementation(this.handleGetDuplications); + jest.mocked(getSources).mockImplementation(this.handleGetSources); + jest.mocked(changeKey).mockImplementation(this.handleChangeKey); } findComponentTree = (key: string, from?: ComponentTree): ComponentTree | undefined => { @@ -625,6 +627,15 @@ export default class ComponentsServiceMock { return this.reply(lines.slice(from - 1, to)); }; + handleChangeKey = (data: { from: string; to: string }) => { + const treeItem = this.components.find(({ component }) => component.key === data.from); + if (treeItem) { + treeItem.component.key = data.to; + return this.reply(undefined); + } + return Promise.reject({ status: 404, message: 'Component not found' }); + }; + reply<T>(response: T): Promise<T> { return Promise.resolve(cloneDeep(response)); } diff --git a/server/sonar-web/src/main/js/apps/projectKey/Key.tsx b/server/sonar-web/src/main/js/apps/projectKey/ProjectKeyApp.tsx index 3015a71080c..d1f7b8e91ff 100644 --- a/server/sonar-web/src/main/js/apps/projectKey/Key.tsx +++ b/server/sonar-web/src/main/js/apps/projectKey/ProjectKeyApp.tsx @@ -32,27 +32,24 @@ interface Props { router: Router; } -export class Key extends React.PureComponent<Props> { - handleChangeKey = (newKey: string) => { - return changeKey({ from: this.props.component.key, to: newKey }).then(() => { - RecentHistory.remove(this.props.component.key); - this.props.router.replace({ pathname: '/project/key', query: { id: newKey } }); +function ProjectKeyApp({ component, router }: Props) { + const handleChangeKey = (newKey: string) => { + return changeKey({ from: component.key, to: newKey }).then(() => { + RecentHistory.remove(component.key); + router.replace({ pathname: '/project/key', query: { id: newKey } }); }); }; - render() { - const { component } = this.props; - return ( - <div className="page page-limited" id="project-key"> - <Helmet defer={false} title={translate('update_key.page')} /> - <header className="page-header"> - <h1 className="page-title">{translate('update_key.page')}</h1> - <div className="page-description">{translate('update_key.page.description')}</div> - </header> - <UpdateForm component={component} onKeyChange={this.handleChangeKey} /> - </div> - ); - } + return ( + <div className="page page-limited" id="project-key"> + <Helmet defer={false} title={translate('update_key.page')} /> + <header className="page-header"> + <h1 className="page-title">{translate('update_key.page')}</h1> + <div className="page-description">{translate('update_key.page.description')}</div> + </header> + <UpdateForm component={component} onKeyChange={handleChangeKey} /> + </div> + ); } -export default withComponentContext(withRouter(Key)); +export default withComponentContext(withRouter(ProjectKeyApp)); diff --git a/server/sonar-web/src/main/js/apps/projectKey/__tests__/Key-test.tsx b/server/sonar-web/src/main/js/apps/projectKey/__tests__/Key-test.tsx deleted file mode 100644 index 0c3767ef775..00000000000 --- a/server/sonar-web/src/main/js/apps/projectKey/__tests__/Key-test.tsx +++ /dev/null @@ -1,44 +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 { changeKey } from '../../../api/components'; -import { mockComponent } from '../../../helpers/mocks/component'; -import { Key } from '../Key'; - -jest.mock('../../../api/components', () => ({ - changeKey: jest.fn().mockResolvedValue(undefined), -})); - -it('should render and change key', async () => { - const withRouterProps = { router: { replace: jest.fn() } as any }; - const wrapper = shallow( - <Key component={mockComponent({ key: 'foo', name: 'Foo' })} {...withRouterProps} /> - ); - expect(wrapper).toMatchSnapshot(); - - wrapper.find('UpdateForm').prop<Function>('onKeyChange')('bar'); - await new Promise(setImmediate); - expect(changeKey).toHaveBeenCalledWith({ from: 'foo', to: 'bar' }); - expect(withRouterProps.router.replace).toHaveBeenCalledWith({ - pathname: '/project/key', - query: { id: 'bar' }, - }); -}); diff --git a/server/sonar-web/src/main/js/apps/projectKey/__tests__/ProjectKeyApp-it.tsx b/server/sonar-web/src/main/js/apps/projectKey/__tests__/ProjectKeyApp-it.tsx new file mode 100644 index 00000000000..d2e829c8039 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectKey/__tests__/ProjectKeyApp-it.tsx @@ -0,0 +1,95 @@ +/* + * 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 { within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { last } from 'lodash'; +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { Route } from 'react-router-dom'; +import { byRole } from 'testing-library-selector'; +import ComponentsServiceMock from '../../../api/mocks/ComponentsServiceMock'; +import { renderAppWithComponentContext } from '../../../helpers/testReactTestingUtils'; +import ProjectKeyApp from '../ProjectKeyApp'; + +jest.mock('../../../api/components'); + +const componentsMock = new ComponentsServiceMock(); + +afterEach(() => { + componentsMock.reset(); +}); + +it('can update project key', async () => { + const { ui, user } = getPageObjects(); + const oldKey = componentsMock.components[0].component.key; + const newKey = 'NEW_KEY'; + renderProjectKeyApp(); + + // Renders + expect(await ui.pageTitle.find()).toBeInTheDocument(); + + // Can type and reset to the old key value + expect(ui.newKeyInput.get()).toHaveValue(oldKey); + await user.clear(ui.newKeyInput.get()); + await user.type(ui.newKeyInput.get(), newKey); + expect(ui.resetInputButton.get()).toBeEnabled(); + await user.click(ui.resetInputButton.get()); + expect(ui.newKeyInput.get()).toHaveValue(oldKey); + + // Can update value + await user.clear(ui.newKeyInput.get()); + await user.type(ui.newKeyInput.get(), newKey); + await user.click(ui.updateInputButton.get()); + // Dialog should show old and new keys + expect(within(ui.updateKeyDialog.get()).getByText(oldKey)).toBeInTheDocument(); + expect(within(ui.updateKeyDialog.get()).getByText(newKey)).toBeInTheDocument(); + await act(async () => { + await user.click(last(ui.updateInputButton.getAll()) as HTMLElement); + }); + expect(ui.updateInputButton.get()).toBeDisabled(); + + expect(ui.newKeyInput.get()).toHaveValue(newKey); +}); + +function renderProjectKeyApp() { + return renderAppWithComponentContext( + 'project/key', + () => <Route path="project/key" element={<ProjectKeyApp />} />, + {}, + { component: componentsMock.components[0].component } + ); +} + +function getPageObjects() { + const user = userEvent.setup(); + + const ui = { + pageTitle: byRole('heading', { name: 'update_key.page' }), + updateKeyDialog: byRole('dialog'), + newKeyInput: byRole('textbox'), + updateInputButton: byRole('button', { name: 'update_verb' }), + resetInputButton: byRole('button', { name: 'reset_verb' }), + }; + + return { + ui, + user, + }; +} diff --git a/server/sonar-web/src/main/js/apps/projectKey/__tests__/UpdateForm-test.tsx b/server/sonar-web/src/main/js/apps/projectKey/__tests__/UpdateForm-test.tsx deleted file mode 100644 index f39b5beada3..00000000000 --- a/server/sonar-web/src/main/js/apps/projectKey/__tests__/UpdateForm-test.tsx +++ /dev/null @@ -1,91 +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, ShallowWrapper } from 'enzyme'; -import * as React from 'react'; -import ProjectKeyInput from '../../../components/common/ProjectKeyInput'; -import { Button, SubmitButton } from '../../../components/controls/buttons'; -import { mockComponent } from '../../../helpers/mocks/component'; -import { click, mockEvent } from '../../../helpers/testUtils'; -import UpdateForm, { UpdateFormProps } from '../UpdateForm'; - -it('should render', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect(getForm(shallowRender())).toMatchSnapshot('form'); -}); - -// eslint-disable-next-line jest/expect-expect -it('should correctly update the form', () => { - const component = mockComponent(); - const wrapper = shallowRender({ component }); - expectButtonDisabled(wrapper, Button).toBe(true); - expectButtonDisabled(wrapper, SubmitButton).toBe(true); - - // Changing the key should unlock the form. - changeInput(wrapper, 'bar'); - expectProjectKeyInputValue(wrapper).toBe('bar'); - expectButtonDisabled(wrapper, Button).toBe(false); - expectButtonDisabled(wrapper, SubmitButton).toBe(false); - - // Changing it back again should lock the form. - changeInput(wrapper, component.key); - expectProjectKeyInputValue(wrapper).toBe(component.key); - expectButtonDisabled(wrapper, Button).toBe(true); - expectButtonDisabled(wrapper, SubmitButton).toBe(true); -}); - -// eslint-disable-next-line jest/expect-expect -it('should correctly reset the form', () => { - const component = mockComponent(); - const wrapper = shallowRender({ component }); - changeInput(wrapper, 'bar'); - click(getForm(wrapper).find(Button)); - expectProjectKeyInputValue(wrapper).toBe(component.key); -}); - -function getForm(wrapper: ShallowWrapper) { - // We're wrapper by a <ConfirmButton>. Dive twice to get the actual form. - return wrapper.dive().dive(); -} - -function expectButtonDisabled( - wrapper: ShallowWrapper, - button: React.ComponentType<{ disabled?: boolean }> -) { - // eslint-disable-next-line jest/valid-expect - return expect(getForm(wrapper).find(button).props().disabled); -} - -function expectProjectKeyInputValue(wrapper: ShallowWrapper) { - // eslint-disable-next-line jest/valid-expect - return expect(getForm(wrapper).find(ProjectKeyInput).props().projectKey); -} - -function changeInput(wrapper: ShallowWrapper, value: string) { - getForm(wrapper) - .find(ProjectKeyInput) - .props() - .onProjectKeyChange(mockEvent({ currentTarget: { value } })); -} - -function shallowRender(props: Partial<UpdateFormProps> = {}) { - return shallow<UpdateFormProps>( - <UpdateForm component={mockComponent()} onKeyChange={jest.fn()} {...props} /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/Key-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/Key-test.tsx.snap deleted file mode 100644 index 5b7c94d6b79..00000000000 --- a/server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/Key-test.tsx.snap +++ /dev/null @@ -1,54 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render and change key 1`] = ` -<div - className="page page-limited" - id="project-key" -> - <Helmet - defer={false} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="update_key.page" - /> - <header - className="page-header" - > - <h1 - className="page-title" - > - update_key.page - </h1> - <div - className="page-description" - > - update_key.page.description - </div> - </header> - <UpdateForm - component={ - { - "breadcrumbs": [], - "key": "foo", - "name": "Foo", - "qualifier": "TRK", - "qualityGate": { - "isDefault": true, - "key": "30", - "name": "Sonar way", - }, - "qualityProfiles": [ - { - "deleted": false, - "key": "my-qp", - "language": "ts", - "name": "Sonar way", - }, - ], - "tags": [], - } - } - onKeyChange={[Function]} - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/UpdateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/UpdateForm-test.tsx.snap deleted file mode 100644 index b52a9d302cd..00000000000 --- a/server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/UpdateForm-test.tsx.snap +++ /dev/null @@ -1,71 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render: default 1`] = ` -<ConfirmButton - confirmButtonText="update_verb" - modalBody={ - <React.Fragment> - update_key.are_you_sure_to_change_key.MyProject - <div - className="spacer-top" - > - update_key.old_key - : - <strong> - my-project - </strong> - </div> - <div - className="spacer-top" - > - update_key.new_key - : - <strong /> - </div> - </React.Fragment> - } - modalHeader="update_key.page" - onConfirm={[MockFunction]} -> - <Component /> -</ConfirmButton> -`; - -exports[`should render: form 1`] = ` -<Fragment> - <form - onSubmit={[Function]} - > - <MandatoryFieldsExplanation - className="spacer-bottom" - /> - <ProjectKeyInput - autofocus={true} - label="update_key.new_key" - onProjectKeyChange={[Function]} - placeholder="update_key.new_key" - projectKey="my-project" - touched={false} - /> - <div - className="spacer-top" - > - <SubmitButton - disabled={true} - id="update-key-submit" - > - update_verb - </SubmitButton> - <Button - className="spacer-left" - disabled={true} - id="update-key-reset" - onClick={[Function]} - type="reset" - > - reset_verb - </Button> - </div> - </form> -</Fragment> -`; |