aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src
diff options
context:
space:
mode:
authorstanislavh <stanislav.honcharov@sonarsource.com>2023-04-13 14:10:07 +0200
committersonartech <sonartech@sonarsource.com>2023-04-14 20:02:47 +0000
commit2768a0aefc190bfa923110a1371c5eeef223ebb0 (patch)
tree8f9999030b1be7d0d997aa551080d9c5d403e6b2 /server/sonar-web/src
parentdb8375a594fd1da6cabae829169fcbb177495432 (diff)
downloadsonarqube-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.ts29
-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.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/projectKey/__tests__/ProjectKeyApp-it.tsx95
-rw-r--r--server/sonar-web/src/main/js/apps/projectKey/__tests__/UpdateForm-test.tsx91
-rw-r--r--server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/Key-test.tsx.snap54
-rw-r--r--server/sonar-web/src/main/js/apps/projectKey/__tests__/__snapshots__/UpdateForm-test.tsx.snap71
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>
-`;