diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateDescriptionPage/UpdateDescriptionPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateDescriptionPage/UpdateDescriptionPage.test.tsx
new file mode 100644
index 00000000000..61c3b04cf6f
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateDescriptionPage/UpdateDescriptionPage.test.tsx
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2024 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { act, fireEvent, render, screen } from '@testing-library/react';
+import React, { forwardRef } from 'react';
+import { postThread } from '../../../rest/feedsAPI';
+import UpdateDescription from './UpdateDescriptionPage';
+
+const mockUseHistory = {
+ push: jest.fn(),
+ goBack: jest.fn(),
+};
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn().mockReturnValue({ entityType: 'table' }),
+ useLocation: jest
+ .fn()
+ .mockReturnValue({ search: '?field=columns&value=shop_id' }),
+ useHistory: jest.fn().mockImplementation(() => mockUseHistory),
+}));
+jest.mock('../../../components/common/ResizablePanels/ResizablePanels', () =>
+ jest.fn().mockImplementation(({ firstPanel, secondPanel }) => (
+ <>
+
{firstPanel.children}
+ {secondPanel.children}
+ >
+ ))
+);
+const mockTableData = {
+ id: 'id1',
+ name: 'dim_location',
+ fullyQualifiedName: 'sample_data.ecommerce_db.shopify.dim_location',
+ description:
+ 'This dimension table contains online shop information. This table contains one shop per row.',
+ tableType: 'Regular',
+ owner: {
+ id: 'id1',
+ name: 'sample_data',
+ type: 'User',
+ },
+ columns: [
+ {
+ name: 'shop_id',
+ dataType: 'NUMERIC',
+ dataTypeDisplay: 'numeric',
+ description:
+ 'Unique identifier for the store. This column is the primary key for this table.',
+ fullyQualifiedName: 'sample_data.ecommerce_db.shopify."dim.shop".shop_id',
+ tags: [],
+ constraint: 'PRIMARY_KEY',
+ ordinalPosition: 1,
+ },
+ ],
+};
+jest.mock('../../../utils/TasksUtils', () => ({
+ fetchEntityDetail: jest
+ .fn()
+ .mockImplementation((_entityType, _decodedEntityFQN, setEntityData) => {
+ setEntityData(mockTableData);
+ }),
+ fetchOptions: jest.fn(),
+ getBreadCrumbList: jest.fn().mockReturnValue([]),
+ getTaskMessage: jest.fn().mockReturnValue('Task message'),
+ getEntityColumnsDetails: jest
+ .fn()
+ .mockImplementation(() => mockTableData.columns),
+ getColumnObject: jest.fn().mockImplementation(() => ({
+ description: mockTableData.columns[0].description,
+ })),
+}));
+jest.mock('../shared/Assignees', () =>
+ jest.fn().mockImplementation(() => Assignees.component
)
+);
+jest.mock(
+ '../../../components/ExploreV1/ExploreSearchCard/ExploreSearchCard',
+ () =>
+ jest.fn().mockImplementation(() => ExploreSearchCard.component
)
+);
+jest.mock(
+ '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component',
+ () => jest.fn().mockImplementation(() => TitleBreadcrumb.component
)
+);
+jest.mock('../../../components/common/RichTextEditor/RichTextEditor', () =>
+ forwardRef(
+ jest.fn().mockImplementation(() => RichTextEditor.component
)
+ )
+);
+jest.mock('../../../rest/feedsAPI', () => ({
+ postThread: jest.fn().mockResolvedValue({}),
+}));
+jest.mock('../../../hooks/useFqn', () => ({
+ useFqn: jest
+ .fn()
+ .mockReturnValue({ fqn: 'sample_data.ecommerce_db.shopify.dim_location' }),
+}));
+
+describe('UpdateDescriptionPage', () => {
+ it('should render component', async () => {
+ render( );
+
+ expect(
+ await screen.findByText('TitleBreadcrumb.component')
+ ).toBeInTheDocument();
+ expect(await screen.findByText('Assignees.component')).toBeInTheDocument();
+ expect(
+ await screen.findByText('RichTextEditor.component')
+ ).toBeInTheDocument();
+ expect(await screen.findByTestId('form-title')).toBeInTheDocument();
+ expect(await screen.findByTestId('form-container')).toBeInTheDocument();
+ expect(await screen.findByTestId('title')).toBeInTheDocument();
+ expect(await screen.findByTestId('cancel-btn')).toBeInTheDocument();
+ expect(await screen.findByTestId('submit-btn')).toBeInTheDocument();
+ });
+
+ it("should go back to previous page when 'Cancel' button is clicked", async () => {
+ render( );
+ const cancelBtn = await screen.findByTestId('cancel-btn');
+
+ act(() => {
+ fireEvent.click(cancelBtn);
+ });
+
+ expect(mockUseHistory.goBack).toHaveBeenCalled();
+ });
+
+ it('should submit form when submit button is clicked', async () => {
+ const mockPostThread = postThread as jest.Mock;
+ render( );
+ const submitBtn = await screen.findByTestId('submit-btn');
+
+ await act(async () => {
+ fireEvent.click(submitBtn);
+ });
+
+ expect(mockPostThread).toHaveBeenCalledWith({
+ about:
+ '<#E::table::sample_data.ecommerce_db.shopify.dim_location::columns::shop_id::description>',
+ from: undefined,
+ message: 'Task message',
+ taskDetails: {
+ assignees: [
+ {
+ id: 'id1',
+ type: 'User',
+ },
+ ],
+ oldValue:
+ 'Unique identifier for the store. This column is the primary key for this table.',
+ suggestion:
+ 'Unique identifier for the store. This column is the primary key for this table.',
+ type: 'UpdateDescription',
+ },
+ type: 'Task',
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateDescriptionPage/UpdateDescriptionPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateDescriptionPage/UpdateDescriptionPage.tsx
index 262113175ef..ba2d2c5c858 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateDescriptionPage/UpdateDescriptionPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateDescriptionPage/UpdateDescriptionPage.tsx
@@ -228,7 +228,11 @@ const UpdateDescription = () => {
entity: t('label.task'),
})}
- {
className="w-full justify-end"
data-testid="cta-buttons"
size={16}>
-
+
{t('label.back')}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateTagPage/UpdateTagPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateTagPage/UpdateTagPage.test.tsx
new file mode 100644
index 00000000000..9fa0924969e
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateTagPage/UpdateTagPage.test.tsx
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2024 Collate.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { act, fireEvent, render, screen } from '@testing-library/react';
+import React from 'react';
+import { MemoryRouter } from 'react-router-dom';
+import { postThread } from '../../../rest/feedsAPI';
+import UpdateTag from './UpdateTagPage';
+
+const mockUseHistory = {
+ push: jest.fn(),
+ goBack: jest.fn(),
+};
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: jest.fn().mockReturnValue({ entityType: 'table' }),
+ useLocation: jest
+ .fn()
+ .mockReturnValue({ search: 'field=columns&value="address.street_name"' }),
+ useHistory: jest.fn().mockImplementation(() => mockUseHistory),
+}));
+jest.mock('../../../components/common/ResizablePanels/ResizablePanels', () =>
+ jest.fn().mockImplementation(({ firstPanel, secondPanel }) => (
+ <>
+ {firstPanel.children}
+ {secondPanel.children}
+ >
+ ))
+);
+const mockTableData = {
+ id: 'id1',
+ name: 'dim_location',
+ fullyQualifiedName: 'sample_data.ecommerce_db.shopify.dim_location',
+ description:
+ 'This dimension table contains online shop information. This table contains one shop per row.',
+ tableType: 'Regular',
+ owner: {
+ id: 'id1',
+ name: 'sample_data',
+ type: 'User',
+ },
+ columns: [
+ {
+ name: 'shop_id',
+ dataType: 'NUMERIC',
+ dataTypeDisplay: 'numeric',
+ description:
+ 'Unique identifier for the store. This column is the primary key for this table.',
+ fullyQualifiedName: 'sample_data.ecommerce_db.shopify."dim.shop".shop_id',
+ tags: [
+ {
+ tagFQN: 'PII.Sensitive',
+ name: 'Sensitive',
+ description:
+ 'PII which if lost, compromised, or disclosed without authorization, could result in substantial harm, embarrassment, inconvenience, or unfairness to an individual.',
+ source: 'Classification',
+ labelType: 'Manual',
+ state: 'Confirmed',
+ },
+ ],
+ constraint: 'PRIMARY_KEY',
+ ordinalPosition: 1,
+ },
+ ],
+};
+jest.mock('../../../utils/TasksUtils', () => ({
+ fetchEntityDetail: jest
+ .fn()
+ .mockImplementation((_entityType, _decodedEntityFQN, setEntityData) => {
+ setEntityData(mockTableData);
+ }),
+ fetchOptions: jest.fn(),
+ getBreadCrumbList: jest.fn().mockReturnValue([]),
+ getTaskMessage: jest.fn().mockReturnValue('Task message'),
+ getEntityColumnsDetails: jest
+ .fn()
+ .mockImplementation(() => mockTableData.columns),
+ getColumnObject: jest.fn().mockImplementation(() => ({
+ tags: mockTableData.columns[0].tags,
+ })),
+}));
+jest.mock('../shared/Assignees', () =>
+ jest.fn().mockImplementation(() => Assignees.component
)
+);
+jest.mock(
+ '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component',
+ () => jest.fn().mockImplementation(() => TitleBreadcrumb.component
)
+);
+jest.mock('../../../rest/feedsAPI', () => ({
+ postThread: jest.fn().mockResolvedValue({}),
+}));
+jest.mock(
+ '../../../components/ExploreV1/ExploreSearchCard/ExploreSearchCard',
+ () =>
+ jest.fn().mockImplementation(() => ExploreSearchCard.component
)
+);
+jest.mock('../shared/TagsTabs', () => ({
+ TagsTabs: jest.fn().mockImplementation(() => TagsTabs.component
),
+}));
+jest.mock('../../../hooks/useFqn', () => ({
+ useFqn: jest
+ .fn()
+ .mockReturnValue({ fqn: 'sample_data.ecommerce_db.shopify.dim_location' }),
+}));
+
+describe('UpdateTagPage', () => {
+ it('should render component', async () => {
+ await act(async () => {
+ render( , { wrapper: MemoryRouter });
+ });
+
+ expect(
+ await screen.findByText('TitleBreadcrumb.component')
+ ).toBeInTheDocument();
+ expect(await screen.findByText('Assignees.component')).toBeInTheDocument();
+ expect(await screen.findByText('TagsTabs.component')).toBeInTheDocument();
+ expect(await screen.findByTestId('form-title')).toBeInTheDocument();
+ expect(await screen.findByTestId('form-container')).toBeInTheDocument();
+ expect(await screen.findByTestId('title')).toBeInTheDocument();
+ expect(await screen.findByTestId('cancel-btn')).toBeInTheDocument();
+ expect(await screen.findByTestId('submit-tag-request')).toBeInTheDocument();
+ });
+
+ it("should go back to previous page when 'Cancel' button is clicked", async () => {
+ render( , { wrapper: MemoryRouter });
+ const cancelBtn = await screen.findByTestId('cancel-btn');
+
+ act(() => {
+ fireEvent.click(cancelBtn);
+ });
+
+ expect(mockUseHistory.goBack).toHaveBeenCalled();
+ });
+
+ it('should submit form when submit button is clicked', async () => {
+ const mockPostThread = postThread as jest.Mock;
+ render( , { wrapper: MemoryRouter });
+
+ const submitBtn = await screen.findByTestId('submit-tag-request');
+
+ await act(async () => {
+ fireEvent.click(submitBtn);
+ });
+
+ expect(mockPostThread).toHaveBeenCalledWith({
+ about:
+ '<#E::table::sample_data.ecommerce_db.shopify.dim_location::columns::"address.street_name"::tags>',
+ from: undefined,
+ message: 'Task message',
+ taskDetails: {
+ assignees: [
+ {
+ id: 'id1',
+ type: 'User',
+ },
+ ],
+ oldValue:
+ // eslint-disable-next-line max-len
+ '[{"tagFQN":"PII.Sensitive","name":"Sensitive","description":"PII which if lost, compromised, or disclosed without authorization, could result in substantial harm, embarrassment, inconvenience, or unfairness to an individual.","source":"Classification","labelType":"Manual","state":"Confirmed"}]',
+ suggestion:
+ // eslint-disable-next-line max-len
+ '[{"tagFQN":"PII.Sensitive","name":"Sensitive","description":"PII which if lost, compromised, or disclosed without authorization, could result in substantial harm, embarrassment, inconvenience, or unfairness to an individual.","source":"Classification","labelType":"Manual","state":"Confirmed"}]',
+ type: 'UpdateTag',
+ },
+ type: 'Task',
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateTagPage/UpdateTagPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateTagPage/UpdateTagPage.tsx
index 964463901a5..cd94619249e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateTagPage/UpdateTagPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/UpdateTagPage/UpdateTagPage.tsx
@@ -240,7 +240,11 @@ const UpdateTag = () => {
entity: t('label.task'),
})}
- {
className="w-full justify-end"
data-testid="cta-buttons"
size={16}>
-
+
{t('label.back')}