diff --git a/packages/dashboard-backend/src/constants/schemas.ts b/packages/dashboard-backend/src/constants/schemas.ts index 3772b8a3d..ece7c6ba6 100644 --- a/packages/dashboard-backend/src/constants/schemas.ts +++ b/packages/dashboard-backend/src/constants/schemas.ts @@ -150,19 +150,6 @@ export const gitConfigSchema: JSONSchema7 = { properties: { gitconfig: { type: 'object', - properties: { - user: { - type: 'object', - properties: { - name: { - type: 'string', - }, - email: { - type: 'string', - }, - }, - }, - }, }, resourceVersion: { type: 'string', diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts index c7c3eab10..3ddd010a8 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/__tests__/index.spec.ts @@ -114,8 +114,8 @@ describe('Gitconfig API', () => { { data: { gitconfig: `[user] -name="User Two" email="user-2@che" +name="User Two" `, }, }, diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts index 56c6286dc..1a4118e22 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/gitConfigApi/index.ts @@ -67,12 +67,10 @@ export class GitConfigApiService implements IGitConfigApi { throw createError(undefined, GITCONFIG_API_ERROR_LABEL, message); } - gitConfig.gitconfig.user.email = changedGitConfig.gitconfig.user.email; - gitConfig.gitconfig.user.name = changedGitConfig.gitconfig.user.name; + gitConfig.gitconfig = changedGitConfig.gitconfig; try { const gitconfigStr = this.fromGitConfig(gitConfig); - const response = await this.coreV1API.patchNamespacedConfigMap( GITCONFIG_CONFIGMAP, namespace, @@ -133,9 +131,7 @@ export class GitConfigApiService implements IGitConfigApi { return { resourceVersion, - gitconfig: { - user: { name: gitconfig.user.name, email: gitconfig.user.email }, - }, + gitconfig, }; } } diff --git a/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts b/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts index 51169a78f..260cff31c 100644 --- a/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts +++ b/packages/dashboard-backend/src/routes/api/__tests__/gitConfig.spec.ts @@ -64,6 +64,11 @@ describe('Gitconfig Routes', () => { name: 'user-che', email: 'user@che', }, + alias: { + co: 'checkout', + st: 'status', + br: 'branch', + }, }, resourceVersion: '123456789', } as api.IGitConfig); diff --git a/packages/dashboard-frontend/package.json b/packages/dashboard-frontend/package.json index 4a5bec535..97bc3224c 100644 --- a/packages/dashboard-frontend/package.json +++ b/packages/dashboard-frontend/package.json @@ -48,6 +48,7 @@ "inversify-inject-decorators": "^3.1.0", "inversify-react": "^1.1.0", "js-yaml": "^4.1.0", + "multi-ini": "^2.3.2", "lodash": "^4.17.21", "process": "^0.11.10", "qs": "^6.12.1", diff --git a/packages/dashboard-frontend/src/Layout/Header/Tools/UserMenu/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/Layout/Header/Tools/UserMenu/__tests__/index.spec.tsx index e1042e79d..37e038f24 100644 --- a/packages/dashboard-frontend/src/Layout/Header/Tools/UserMenu/__tests__/index.spec.tsx +++ b/packages/dashboard-frontend/src/Layout/Header/Tools/UserMenu/__tests__/index.spec.tsx @@ -82,7 +82,7 @@ describe('User Menu', () => { const logoutItem = screen.getByRole('menuitem', { name: /logout/i }); fireEvent.click(logoutItem); - expect(mockLogout).toBeCalled(); + expect(mockLogout).toHaveBeenCalled(); }); }); diff --git a/packages/dashboard-frontend/src/components/WorkspaceLogs/ViewerTools/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/components/WorkspaceLogs/ViewerTools/__tests__/index.spec.tsx index 4f15dea7d..02c56b663 100644 --- a/packages/dashboard-frontend/src/components/WorkspaceLogs/ViewerTools/__tests__/index.spec.tsx +++ b/packages/dashboard-frontend/src/components/WorkspaceLogs/ViewerTools/__tests__/index.spec.tsx @@ -40,7 +40,7 @@ describe('The WorkspaceLogsTerminalTools component', () => { const downloadButton = screen.getByRole('button', { name: 'Download' }); downloadButton.click(); - expect(mockOnDownload).toBeCalled(); + expect(mockOnDownload).toHaveBeenCalled(); }); it('should handle expand button click', () => { @@ -49,16 +49,16 @@ describe('The WorkspaceLogsTerminalTools component', () => { const expandButton = screen.getByRole('button', { name: 'Expand' }); expandButton.click(); - expect(mockOnToggle).toBeCalledTimes(1); - expect(mockHideAll).toBeCalled(); + expect(mockOnToggle).toHaveBeenCalledTimes(1); + expect(mockHideAll).toHaveBeenCalled(); const compressButton = screen.queryByRole('button', { name: 'Compress' }); expect(compressButton).not.toBeNull(); compressButton?.click(); - expect(mockShowAll).toBeCalled(); - expect(mockOnToggle).toBeCalledTimes(2); + expect(mockShowAll).toHaveBeenCalled(); + expect(mockOnToggle).toHaveBeenCalledTimes(2); }); }); diff --git a/packages/dashboard-frontend/src/components/WorkspaceProgress/CommonSteps/CheckRunningWorkspacesLimit/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/components/WorkspaceProgress/CommonSteps/CheckRunningWorkspacesLimit/__tests__/index.spec.tsx index 3b37fd8a7..310614cd7 100644 --- a/packages/dashboard-frontend/src/components/WorkspaceProgress/CommonSteps/CheckRunningWorkspacesLimit/__tests__/index.spec.tsx +++ b/packages/dashboard-frontend/src/components/WorkspaceProgress/CommonSteps/CheckRunningWorkspacesLimit/__tests__/index.spec.tsx @@ -348,7 +348,7 @@ describe('Common steps, check running workspaces limit', () => { await jest.runOnlyPendingTimersAsync(); // switch to the next step - await waitFor(() => expect(mockOnNextStep).toBeCalled()); + await waitFor(() => expect(mockOnNextStep).toHaveBeenCalled()); expect(mockOnError).not.toHaveBeenCalled(); expect(mockOnRestart).not.toHaveBeenCalled(); }); diff --git a/packages/dashboard-frontend/src/components/WorkspaceProgress/CreatingSteps/Initialize/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/components/WorkspaceProgress/CreatingSteps/Initialize/__tests__/index.spec.tsx index 917eb17d9..bc730c412 100644 --- a/packages/dashboard-frontend/src/components/WorkspaceProgress/CreatingSteps/Initialize/__tests__/index.spec.tsx +++ b/packages/dashboard-frontend/src/components/WorkspaceProgress/CreatingSteps/Initialize/__tests__/index.spec.tsx @@ -267,7 +267,7 @@ describe('Creating steps, initializing', () => { await jest.advanceTimersByTimeAsync(MIN_STEP_DURATION_MS); - await waitFor(() => expect(mockOnError).not.toBeCalled()); + await waitFor(() => expect(mockOnError).not.toHaveBeenCalled()); expect(mockOnNextStep).toHaveBeenCalled(); }); diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/DeleteRegistriesModal.spec.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/DeleteRegistriesModal.spec.tsx index 3ba86ed76..1f2fe6da9 100644 --- a/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/DeleteRegistriesModal.spec.tsx +++ b/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/DeleteRegistriesModal.spec.tsx @@ -132,6 +132,6 @@ describe('Delete Registries Modal', () => { const cancelButton = screen.getByTestId('cancel-button'); userEvent.click(cancelButton); - expect(mockOnCancel).toBeCalled(); + expect(mockOnCancel).toHaveBeenCalled(); }); }); diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/EditRegistryModal.spec.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/EditRegistryModal.spec.tsx index 6868ceb29..a9334d41b 100644 --- a/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/EditRegistryModal.spec.tsx +++ b/packages/dashboard-frontend/src/pages/UserPreferences/ContainerRegistriesTab/Modals/__tests__/EditRegistryModal.spec.tsx @@ -103,6 +103,6 @@ describe('Edit Registry Modal', () => { const cancelButton = screen.getByTestId('cancel-button'); userEvent.click(cancelButton); - expect(mockOnCancel).toBeCalled(); + expect(mockOnCancel).toHaveBeenCalled(); }); }); diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__mocks__/index.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__mocks__/index.tsx new file mode 100644 index 000000000..e51721c7d --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__mocks__/index.tsx @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2024 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import React from 'react'; + +import { Props } from '@/pages/UserPreferences/GitConfig/AddModal/GitConfigForm'; +import * as GitConfigStore from '@/store/GitConfig'; + +export class GitConfigForm extends React.PureComponent { + render() { + const { onChange } = this.props; + + return ( +
+ + onChange({ user: { name: 'User One' } } as GitConfigStore.GitConfig, false) + } + /> + + onChange({ user: { email: 'user-1@chetest.com', name: 'User One' } }, true) + } + /> +
+ ); + } +} diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/__snapshots__/index.spec.tsx.snap b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/__snapshots__/index.spec.tsx.snap new file mode 100644 index 000000000..daccc75ae --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/__snapshots__/index.spec.tsx.snap @@ -0,0 +1,95 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GitConfigForm snapshot with predefined git configuration 1`] = ` +
+
+
+ + +
+
+
+ + +
+ +
+
+
+`; + +exports[`GitConfigForm snapshot without predefined git configuration 1`] = ` +
+
+
+ + +
+
+
+ + +
+ +
+
+
+`; diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/index.spec.tsx new file mode 100644 index 000000000..95654171b --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/__tests__/index.spec.tsx @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-2024 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import getComponentRenderer, { screen } from '@/services/__mocks__/getComponentRenderer'; +import * as GitConfigStore from '@/store/GitConfig'; + +import { GitConfigForm } from '..'; + +const mockOnChange = jest.fn(); + +jest.mock('@/pages/UserPreferences/GitConfig/GitConfigImport'); + +const { renderComponent, createSnapshot } = getComponentRenderer(getComponent); + +describe('GitConfigForm', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('snapshot without predefined git configuration', () => { + const snapshot = createSnapshot(); + expect(snapshot.toJSON()).toMatchSnapshot(); + }); + test('snapshot with predefined git configuration', () => { + const snapshot = createSnapshot({ user: { email: 'user-1@chetest.com', name: 'User One' } }); + expect(snapshot.toJSON()).toMatchSnapshot(); + }); + + it('should handle a valid value', () => { + renderComponent(); + + const gitConfigField = screen.getByTestId('submit-valid-git-config'); + userEvent.click(gitConfigField); + + expect(mockOnChange).toHaveBeenCalledWith( + { user: { email: 'user-1@chetest.com', name: 'User One' } }, + true, + ); + }); + + it('should handle an invalid value', () => { + renderComponent(); + + const gitConfigField = screen.getByTestId('submit-invalid-git-config'); + userEvent.click(gitConfigField); + + expect(mockOnChange).toHaveBeenCalledWith({ user: { name: 'User One' } }, false); + }); +}); + +function getComponent(gitConfig?: GitConfigStore.GitConfig) { + return ; +} diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/index.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/index.tsx new file mode 100644 index 000000000..fea21d0e3 --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/GitConfigForm/index.tsx @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018-2024 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import { Form, FormGroup, ValidatedOptions } from '@patternfly/react-core'; +import * as ini from 'multi-ini'; +import React from 'react'; + +import { GitConfigImport } from '@/pages/UserPreferences/GitConfig/GitConfigImport'; +import * as GitConfigStore from '@/store/GitConfig'; + +export const REQUIRED_ERROR = 'This field is required.'; +const MAX_LENGTH = 4096; +export const MAX_LENGTH_ERROR = `The value is too long. The maximum length is ${MAX_LENGTH} characters.`; +export const WRONG_TYPE_ERROR = 'This file type is not supported.'; + +export type Props = { + gitConfig: GitConfigStore.GitConfig | undefined; + onChange: (gitConfig: GitConfigStore.GitConfig, isValid: boolean) => void; +}; + +export type State = { + isUpload: boolean; + gitConfig: GitConfigStore.GitConfig | undefined; + gitConfigStr: string | undefined; + validated: ValidatedOptions; +}; + +export class GitConfigForm extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { + isUpload: false, + gitConfig: undefined, + gitConfigStr: undefined, + validated: ValidatedOptions.default, + }; + } + + public componentDidMount() { + const { gitConfig } = this.props; + const gitConfigStr = this.stringifyGitConfig(gitConfig); + if (gitConfigStr !== undefined) { + this.onChange(gitConfigStr, false); + } + } + + public shouldComponentUpdate(nextProps: Readonly, nextState: Readonly): boolean { + const { gitConfig, validated } = this.state; + const { gitConfig: nextGitConfig, validated: nextValidated } = nextState; + + return gitConfig !== nextGitConfig || validated !== nextValidated; + } + + private parseGitConfig(gitConfigStr: string): GitConfigStore.GitConfig { + const parser = new ini.Parser(); + const gitConfigLines = gitConfigStr.split(/\r?\n/); + const gitConfig = parser.parse(gitConfigLines); + return gitConfig as GitConfigStore.GitConfig; + } + + private stringifyGitConfig(gitConfig: GitConfigStore.GitConfig | undefined): string | undefined { + if (gitConfig === undefined) { + return undefined; + } + const serializer = new ini.Serializer(); + return serializer + .serialize(gitConfig) + .replace(/\n/g, '\n ') + .replace(/ {4}\[/g, '['); + } + + private onChange(gitConfigStr: string, isUpload: boolean): void { + const { onChange } = this.props; + try { + const gitConfig = this.parseGitConfig(gitConfigStr); + const validated = this.validate(gitConfig); + const isValid = validated === ValidatedOptions.success; + + this.setState({ gitConfigStr, gitConfig, validated, isUpload }); + onChange(gitConfig, isValid); + } catch (error) { + console.log(error); + this.setState({ validated: ValidatedOptions.error, isUpload }); + } + } + + private validate(gitConfig: GitConfigStore.GitConfig): ValidatedOptions { + return this.isGitConfig(gitConfig) ? ValidatedOptions.success : ValidatedOptions.error; + } + + private isGitConfig(gitConfig: unknown): gitConfig is GitConfigStore.GitConfig { + return ( + (gitConfig as GitConfigStore.GitConfig).user !== undefined && + (gitConfig as GitConfigStore.GitConfig).user.email !== undefined && + (gitConfig as GitConfigStore.GitConfig).user.name !== undefined + ); + } + + private getErrorMessage(gitConfigStr: string | undefined, isUpload: boolean): string | undefined { + if (gitConfigStr && gitConfigStr.length > MAX_LENGTH) { + return MAX_LENGTH_ERROR; + } + if (isUpload) { + return WRONG_TYPE_ERROR; + } + return REQUIRED_ERROR; + } + + public render(): React.ReactElement { + const { validated, gitConfigStr, isUpload } = this.state; + + const errorMessage = this.getErrorMessage(gitConfigStr, isUpload); + + const content = this.stringifyGitConfig(this.props.gitConfig); + + return ( +
e.preventDefault()}> + + this.onChange(gitConfig, isUpload)} + /> + +
+ ); + } +} diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/__tests__/index.spec.tsx new file mode 100644 index 000000000..8f5b698bb --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/__tests__/index.spec.tsx @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018-2024 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +/* eslint-disable @typescript-eslint/no-non-null-assertion */ + +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import getComponentRenderer, { screen } from '@/services/__mocks__/getComponentRenderer'; + +import { GitConfigAddModal } from '..'; + +const { renderComponent } = getComponentRenderer(getComponent); + +jest.mock('@/pages/UserPreferences/GitConfig/GitConfigImport'); + +const mockOnSave = jest.fn(); +const mockOnClose = jest.fn(); + +describe('AddModal', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('modal is hidden', () => { + renderComponent(false); + + expect(screen.queryByRole('dialog')).toBeFalsy(); + }); + + test('modal is visible', () => { + renderComponent(true); + + expect(screen.queryByRole('dialog')).toBeTruthy(); + }); + + it('should handle click on Close button', () => { + renderComponent(true); + + const closeButton = screen.queryByRole('button', { name: 'Close' }); + expect(closeButton).toBeTruthy(); + + userEvent.click(closeButton!); + expect(mockOnClose).toHaveBeenCalledTimes(1); + }); + + it('should handle click on Cancel button', () => { + renderComponent(true); + + const cancelButton = screen.queryByRole('button', { name: 'Cancel' }); + expect(cancelButton).toBeTruthy(); + + userEvent.click(cancelButton!); + expect(mockOnClose).toHaveBeenCalledTimes(1); + }); + + describe('modal window', () => { + const isOpen = true; + + test('modal title', () => { + renderComponent(isOpen); + + expect( + screen.queryByRole('heading', { + name: 'Import Git Configuration', + }), + ).toBeTruthy(); + }); + + test('modal footer', () => { + renderComponent(isOpen); + + expect( + screen.queryByRole('button', { + name: 'Add', + }), + ).toBeTruthy(); + expect( + screen.queryByRole('button', { + name: 'Cancel', + }), + ).toBeTruthy(); + }); + }); + + describe('should handle saving git configuration', () => { + const isOpen = true; + + it('should handle valid git configuration', () => { + renderComponent(isOpen); + + // expect add button to be disabled + const addButton = screen.getByRole('button', { name: 'Add' }); + expect(addButton).toBeDisabled(); + + const SubmitValidFormButton = screen.getByTestId('submit-valid-git-config'); + userEvent.click(SubmitValidFormButton); + + // expect add button to be enabled + expect(addButton).toBeEnabled(); + + userEvent.click(addButton); + + // expect onSave to be called + expect(mockOnSave).toHaveBeenCalledTimes(1); + }); + + it('should handle invalid git configuration', () => { + renderComponent(isOpen); + + // expect add button to be enabled + const addButton = screen.getByRole('button', { name: 'Add' }); + expect(addButton).toBeDisabled(); + + const SubmitInvalidFormButton = screen.getByTestId('submit-invalid-git-config'); + userEvent.click(SubmitInvalidFormButton); + + // expect add button to be disabled + expect(addButton).toBeDisabled(); + }); + }); +}); + +function getComponent(isOpen: boolean): React.ReactElement { + return ( + + ); +} diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/index.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/index.tsx new file mode 100644 index 000000000..12f215851 --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/AddModal/index.tsx @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018-2024 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import { Button, ButtonVariant, Modal, ModalVariant } from '@patternfly/react-core'; +import isEqual from 'lodash/isEqual'; +import React from 'react'; + +import { GitConfigForm } from '@/pages/UserPreferences/GitConfig/AddModal/GitConfigForm'; +import * as GitConfigStore from '@/store/GitConfig'; + +export type Props = { + gitConfig: GitConfigStore.GitConfig | undefined; + isOpen: boolean; + onSave: (gitConfig: GitConfigStore.GitConfig) => Promise; + onCloseModal: () => void; +}; +export type State = { + gitConfig: GitConfigStore.GitConfig | undefined; + isSaveEnabled: boolean; +}; + +export class GitConfigAddModal extends React.PureComponent { + constructor(props: Props) { + super(props); + + this.state = { + gitConfig: undefined, + // initially disabled until something changes and form is valid + isSaveEnabled: false, + }; + } + + private handleSaveGitConfig(): void { + const { gitConfig } = this.state; + + if (gitConfig) { + this.props.onSave(gitConfig); + } + } + + private handleCloseModal(): void { + this.props.onCloseModal(); + } + + private handleChangeGitConfig( + gitConfig: GitConfigStore.GitConfig | undefined, + isValid: boolean, + ): void { + const isSaveEnabled = + isValid && gitConfig !== undefined && !isEqual(this.props.gitConfig, gitConfig); + this.setState({ + gitConfig, + isSaveEnabled, + }); + } + + private buildModalFooter(): React.ReactNode { + return ( + + + + + ); + } + + public render(): React.ReactElement { + const { isOpen } = this.props; + + const modalTitle = 'Import Git Configuration'; + const modalFooter = this.buildModalFooter(); + + return ( + this.handleCloseModal()} + footer={modalFooter} + > + this.handleChangeGitConfig(gitConfig, isValid)} + /> + + ); + } +} diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/GitConfigImport/__mocks__/index.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/GitConfigImport/__mocks__/index.tsx new file mode 100644 index 000000000..22f3f0c71 --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/GitConfigImport/__mocks__/index.tsx @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2024 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ + +import React from 'react'; + +import { Props } from '@/pages/UserPreferences/GitConfig/GitConfigImport'; + +export class GitConfigImport extends React.PureComponent { + render() { + const { content, onChange } = this.props; + + return ( +
+ onChange('[user]\n\tname = User One\n', false)} + /> + + onChange('[user]\n\tname = User One\n\temail = user-1@chetest.com\n', true) + } + /> +
+ ); + } +} diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/GitConfigImport/__tests__/__snapshots__/index.spec.tsx.snap b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/GitConfigImport/__tests__/__snapshots__/index.spec.tsx.snap new file mode 100644 index 000000000..49f55bf14 --- /dev/null +++ b/packages/dashboard-frontend/src/pages/UserPreferences/GitConfig/GitConfigImport/__tests__/__snapshots__/index.spec.tsx.snap @@ -0,0 +1,324 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GitConfigUpload snapshot with content, validation is in the default state 1`] = ` +
+
+
+ + + +
+
+
+