diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx
index 23a0391cb12..1b61080b03e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx
@@ -387,17 +387,12 @@ const ObservabilityAlertsPage = withSuspenseFallback(
)
);
-const ObservabilityAlertDetailsPage = withSuspenseFallback(
- React.lazy(
- () =>
- import(
- '../../pages/ObservabilityAlertDetailsPage/ObservabilityAlertDetailsPage'
- )
- )
+const AlertDetailsPage = withSuspenseFallback(
+ React.lazy(() => import('../../pages/AlertDetailsPage/AlertDetailsPage'))
);
const NotificationsAlertDetailsPage = () => (
-
+
);
const AddObservabilityPage = withSuspenseFallback(
@@ -1070,7 +1065,7 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddNotificationPage/AddNotificationPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddNotificationPage/AddNotificationPage.test.tsx
new file mode 100644
index 00000000000..38bff4a0d25
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddNotificationPage/AddNotificationPage.test.tsx
@@ -0,0 +1,134 @@
+/*
+ * 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 AddNotificationPage from './AddNotificationPage';
+
+const mockPush = jest.fn();
+const mockGoBack = jest.fn();
+
+jest.mock('../../rest/alertsAPI', () => ({
+ getAlertsFromName: jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ name: 'ActivityFeedAlert',
+ })
+ ),
+ getResourceFunctions: jest.fn(),
+ updateNotificationAlert: jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ id: 'ActivityFeedAlert',
+ data: [],
+ })
+ ),
+ createNotificationAlert: jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ alert: 'Notification',
+ })
+ ),
+}));
+
+jest.mock('../../utils/RouterUtils', () => ({
+ getNotificationAlertDetailsPath: jest.fn(),
+ getSettingPath: jest.fn(),
+}));
+
+jest.mock('../../components/common/ResizablePanels/ResizablePanels', () =>
+ jest.fn().mockImplementation(({ firstPanel, secondPanel }) => (
+ <>
+
{firstPanel.children}
+ {secondPanel.children}
+ >
+ ))
+);
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useHistory: jest.fn().mockImplementation(() => ({
+ push: mockPush,
+ goBack: mockGoBack,
+ })),
+}));
+
+jest.mock('../../hooks/useFqn', () => ({
+ useFqn: jest.fn().mockReturnValue({ fqn: '' }),
+}));
+
+describe('AddNotificationPage', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render Add Notification Page', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ expect(
+ await screen.findByText('label.notification-plural')
+ ).toBeInTheDocument();
+ });
+
+ it('should display the correct breadcrumb', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+ const breadcrumbLinks = screen.getAllByTestId('breadcrumb-link');
+
+ expect(breadcrumbLinks[0]).toHaveTextContent('label.setting-plural');
+ expect(breadcrumbLinks[1]).toHaveTextContent('label.notification-plural');
+ expect(breadcrumbLinks[2]).toHaveTextContent('label.create-entity');
+ });
+
+ it('should display SubTitle', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ expect(
+ await screen.findByText(/message.alerts-description/)
+ ).toBeInTheDocument();
+ });
+
+ it('should render Add alert button', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ expect(await screen.findByText(/label.create-entity/)).toBeInTheDocument();
+ });
+
+ it('should navigate back when the cancel button is clicked', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ const cancelButton = screen.getByTestId('cancel-button');
+
+ await act(async () => {
+ fireEvent.click(cancelButton);
+ });
+
+ expect(mockGoBack).toHaveBeenCalled();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/AddObservabilityPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/AddObservabilityPage.test.tsx
new file mode 100644
index 00000000000..6969f8c033e
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/AddObservabilityPage.test.tsx
@@ -0,0 +1,150 @@
+/*
+ * 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 AddObservabilityPage from './AddObservabilityPage';
+
+const MOCK_DATA = [
+ {
+ id: '971a21b3-eeaf-4765-bda7-4e2cdb9788de',
+ name: 'alert-test',
+ fullyQualifiedName: 'alert-test',
+ href: 'http://localhost:8585/api/v1/events/subscriptions/971a21b3-eeaf-4765-bda7-4e2cdb9788de',
+ version: 0.1,
+ updatedAt: 1682366749021,
+ updatedBy: 'admin',
+ filteringRules: {
+ resources: ['all'],
+ rules: [
+ {
+ name: 'matchIngestionPipelineState',
+ effect: 'include',
+ condition: "matchIngestionPipelineState('partialSuccess')",
+ },
+ ],
+ },
+ subscriptionType: 'Email',
+ subscriptionConfig: {
+ receivers: ['test@gmail.com'],
+ },
+ enabled: true,
+ batchSize: 10,
+ timeout: 10,
+ readTimeout: 12,
+ deleted: false,
+ provider: 'user',
+ },
+];
+const mockPush = jest.fn();
+const mockGoBack = jest.fn();
+
+jest.mock('../../rest/observabilityAPI', () => ({
+ getObservabilityAlertByFQN: jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ fqn: 'alert-test',
+ })
+ ),
+ createObservabilityAlert: jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ alert: 'Observability',
+ })
+ ),
+ getResourceFunctions: jest.fn(),
+ updateObservabilityAlert: jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ id: 'test',
+ jsonPatch: MOCK_DATA,
+ })
+ ),
+}));
+
+jest.mock('../../components/common/ResizablePanels/ResizablePanels', () =>
+ jest.fn().mockImplementation(({ firstPanel, secondPanel }) => (
+ <>
+ {firstPanel.children}
+ {secondPanel.children}
+ >
+ ))
+);
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useHistory: jest.fn().mockImplementation(() => ({
+ push: mockPush,
+ goBack: mockGoBack,
+ })),
+}));
+
+describe('Add ObservabilityPage Alerts Page Tests', () => {
+ it('should render Add Observability Page', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ expect(await screen.findByText('label.observability')).toBeInTheDocument();
+ });
+
+ it('should display SubTitle', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ expect(
+ await screen.findByText(/message.alerts-description/)
+ ).toBeInTheDocument();
+ });
+
+ it('should render Add alert button', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ expect(await screen.findByText(/label.create-entity/)).toBeInTheDocument();
+ });
+
+ it('should display the correct breadcrumb', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+ const breadcrumbLinks = screen.getAllByTestId('breadcrumb-link');
+
+ expect(breadcrumbLinks[0]).toHaveTextContent('label.observability');
+ expect(breadcrumbLinks[1]).toHaveTextContent('label.alert-plural');
+ expect(breadcrumbLinks[2]).toHaveTextContent('label.create-entity');
+ });
+
+ it('should navigate back when the cancel button is clicked', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ const cancelButton = screen.getByTestId('cancel-button');
+
+ await act(async () => {
+ fireEvent.click(cancelButton);
+ });
+
+ expect(mockGoBack).toHaveBeenCalled();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/DestinationFormItem/DestinationFormItem.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/DestinationFormItem/DestinationFormItem.test.tsx
new file mode 100644
index 00000000000..fdacdeb0ca9
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/DestinationFormItem/DestinationFormItem.test.tsx
@@ -0,0 +1,100 @@
+/*
+ * 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 { render, screen } from '@testing-library/react';
+import { Form, FormInstance } from 'antd';
+import React from 'react';
+import DestinationFormItem from './DestinationFormItem.component';
+
+jest.mock('../../../utils/Alerts/AlertsUtil', () => ({
+ getDestinationConfigField: jest
+ .fn()
+ .mockReturnValue(),
+ getSubscriptionTypeOptions: jest.fn().mockReturnValue([]),
+ listLengthValidator: jest.fn().mockImplementation(() => Promise.resolve()),
+}));
+
+jest.mock('../../../utils/ObservabilityUtils', () => ({
+ checkIfDestinationIsInternal: jest.fn().mockImplementation(() => false),
+ getAlertDestinationCategoryIcons: jest
+ .fn()
+ .mockImplementation(() => Icon),
+}));
+
+const mockProps = {
+ heading: 'heading',
+ subHeading: 'subHeading',
+ buttonLabel: 'buttonLabel',
+};
+
+describe('DestinationFormItem', () => {
+ it('should renders without crashing', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => ['container']);
+
+ render();
+
+ expect(screen.getByText('heading')).toBeInTheDocument();
+ expect(screen.getByText('subHeading')).toBeInTheDocument();
+ expect(screen.getByText('buttonLabel')).toBeInTheDocument();
+
+ expect(screen.getByTestId('add-destination-button')).toBeInTheDocument();
+ });
+
+ it('add destination button should be disabled if there is no selected trigger', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => []);
+
+ render();
+
+ expect(screen.getByTestId('add-destination-button')).toBeDisabled();
+ });
+
+ it('add destination button should be enabled if there is selected trigger', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => ['container']);
+
+ render();
+
+ expect(screen.getByTestId('add-destination-button')).toBeEnabled();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormActionItem/ObservabilityFormActionItem.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormActionItem/ObservabilityFormActionItem.test.tsx
new file mode 100644
index 00000000000..9004b37cb66
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormActionItem/ObservabilityFormActionItem.test.tsx
@@ -0,0 +1,109 @@
+/*
+ * 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 { render, screen } from '@testing-library/react';
+import { Form, FormInstance } from 'antd';
+import React from 'react';
+import { EventFilterRule } from '../../../generated/events/eventSubscription';
+import { MOCK_FILTER_RESOURCES } from '../../../test/unit/mocks/observability.mock';
+import ObservabilityFormActionItem from './ObservabilityFormActionItem';
+
+jest.mock('../../../utils/Alerts/AlertsUtil', () => ({
+ getConditionalField: jest
+ .fn()
+ .mockReturnValue(),
+ getSupportedFilterOptions: jest.fn().mockReturnValue([]),
+}));
+
+const mockSupportedActions = MOCK_FILTER_RESOURCES.reduce(
+ (resource, current) => {
+ resource.push(...(current.supportedActions ?? []));
+
+ return resource;
+ },
+ [] as EventFilterRule[]
+);
+
+describe('ObservabilityFormActionItem', () => {
+ it('should renders without crashing', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => ['container']);
+
+ render(
+
+ );
+
+ expect(screen.getByText('label.action-plural')).toBeInTheDocument();
+ expect(
+ screen.getByText('message.alerts-action-description')
+ ).toBeInTheDocument();
+
+ expect(screen.getByTestId('actions-list')).toBeInTheDocument();
+ expect(screen.getByTestId('add-actions')).toBeInTheDocument();
+ });
+
+ it('add actions button should be disabled if there is no selected trigger and filters', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => []);
+
+ render(
+
+ );
+
+ const addButton = screen.getByTestId('add-actions');
+
+ expect(addButton).toBeDisabled();
+ });
+
+ it('add actions button should not be disabled if there is selected trigger and filters', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => ['container']);
+
+ render(
+
+ );
+
+ const addButton = screen.getByTestId('add-actions');
+
+ expect(addButton).not.toBeDisabled();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormActionItem/ObservabilityFormActionItem.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormActionItem/ObservabilityFormActionItem.tsx
index d08164a3dff..127f5200534 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormActionItem/ObservabilityFormActionItem.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormActionItem/ObservabilityFormActionItem.tsx
@@ -67,7 +67,7 @@ function ObservabilityFormActionItem({
fields.length < (supportedActions?.length ?? 1);
return (
-
+
{fields.map(({ key, name }) => {
const effect =
form.getFieldValue([
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormFiltersItem/ObservabilityFormFiltersItem.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormFiltersItem/ObservabilityFormFiltersItem.test.tsx
new file mode 100644
index 00000000000..0aef830c9df
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormFiltersItem/ObservabilityFormFiltersItem.test.tsx
@@ -0,0 +1,109 @@
+/*
+ * 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 { render, screen } from '@testing-library/react';
+import { Form, FormInstance } from 'antd';
+import React from 'react';
+import { EventFilterRule } from '../../../generated/events/eventSubscription';
+import { MOCK_FILTER_RESOURCES } from '../../../test/unit/mocks/observability.mock';
+import ObservabilityFormFiltersItem from './ObservabilityFormFiltersItem';
+
+jest.mock('../../../utils/Alerts/AlertsUtil', () => ({
+ getConditionalField: jest
+ .fn()
+ .mockReturnValue(),
+ getSupportedFilterOptions: jest.fn().mockReturnValue([]),
+}));
+
+const mockSupportedFilters = MOCK_FILTER_RESOURCES.reduce(
+ (resource, current) => {
+ resource.push(...(current.supportedFilters ?? []));
+
+ return resource;
+ },
+ [] as EventFilterRule[]
+);
+
+describe('ObservabilityFormFiltersItem', () => {
+ it('should renders without crashing', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => ['container']);
+
+ render(
+
+ );
+
+ expect(screen.getByText('label.filter-plural')).toBeInTheDocument();
+ expect(
+ screen.getByText('message.alerts-filter-description')
+ ).toBeInTheDocument();
+
+ expect(screen.getByTestId('filters-list')).toBeInTheDocument();
+ expect(screen.getByTestId('add-filters')).toBeInTheDocument();
+ });
+
+ it('add filter button should be disabled if there is no selected trigger', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => []);
+
+ render(
+
+ );
+
+ const addButton = screen.getByTestId('add-filters');
+
+ expect(addButton).toBeDisabled();
+ });
+
+ it('add filter button should not be disabled if there is selected trigger', () => {
+ const setFieldValue = jest.fn();
+ const getFieldValue = jest.fn();
+ jest.spyOn(Form, 'useFormInstance').mockImplementation(
+ () =>
+ ({
+ setFieldValue,
+ getFieldValue,
+ } as unknown as FormInstance)
+ );
+
+ const useWatchMock = jest.spyOn(Form, 'useWatch');
+ useWatchMock.mockImplementation(() => ['container']);
+
+ render(
+
+ );
+
+ const addButton = screen.getByTestId('add-filters');
+
+ expect(addButton).not.toBeDisabled();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormTriggerItem/ObservabilityFormTriggerItem.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormTriggerItem/ObservabilityFormTriggerItem.test.tsx
new file mode 100644
index 00000000000..2f46ba25ec2
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddObservabilityPage/ObservabilityFormTriggerItem/ObservabilityFormTriggerItem.test.tsx
@@ -0,0 +1,81 @@
+/*
+ * 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 { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+import { MemoryRouter } from 'react-router-dom';
+import { useFqn } from '../../../hooks/useFqn';
+import { MOCK_FILTER_RESOURCES } from '../../../test/unit/mocks/observability.mock';
+import ObservabilityFormTriggerItem from './ObservabilityFormTriggerItem';
+
+jest.mock('../../../hooks/useFqn', () => ({
+ useFqn: jest.fn().mockReturnValue({ fqn: '' }),
+}));
+
+jest.mock('antd', () => {
+ const antd = jest.requireActual('antd');
+
+ return {
+ ...antd,
+ Form: {
+ ...antd.Form,
+ useFormInstance: jest.fn().mockImplementation(() => ({
+ setFieldValue: jest.fn(),
+ getFieldValue: jest.fn(),
+ })),
+ },
+ };
+});
+
+describe('ObservabilityFormTriggerItem', () => {
+ it('should renders without crashing', () => {
+ render(
+ ,
+ { wrapper: MemoryRouter }
+ );
+
+ expect(screen.getByText('label.trigger')).toBeInTheDocument();
+ expect(
+ screen.getByText('message.alerts-trigger-description')
+ ).toBeInTheDocument();
+
+ expect(screen.getByTestId('add-trigger-button')).toBeInTheDocument();
+ });
+
+ it('should render the trigger select when fqn is provided', () => {
+ (useFqn as jest.Mock).mockImplementationOnce(() => ({
+ fqn: 'test',
+ }));
+
+ render(
+ ,
+ { wrapper: MemoryRouter }
+ );
+
+ expect(screen.getByTestId('trigger-select')).toBeInTheDocument();
+ });
+
+ it('should display select dropdown when clicked on add trigger button', async () => {
+ render(
+ ,
+ { wrapper: MemoryRouter }
+ );
+ const addButton = screen.getByTestId('add-trigger-button');
+ await act(async () => {
+ userEvent.click(addButton);
+ });
+
+ expect(screen.getByTestId('drop-down-menu')).toBeInTheDocument();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertDetailsPage/ObservabilityAlertDetailsPage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.interface.ts
similarity index 92%
rename from openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertDetailsPage/ObservabilityAlertDetailsPage.interface.ts
rename to openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.interface.ts
index 56cb668a53a..ab62564d7b3 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertDetailsPage/ObservabilityAlertDetailsPage.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.interface.ts
@@ -11,6 +11,6 @@
* limitations under the License.
*/
-export interface ObservabilityAlertDetailsPageProps {
+export interface AlertDetailsPageProps {
isNotificationAlert: boolean;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertDetailsPage/ObservabilityAlertDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.tsx
similarity index 98%
rename from openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertDetailsPage/ObservabilityAlertDetailsPage.tsx
rename to openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.tsx
index ff9d903a4a7..f3badabccc0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertDetailsPage/ObservabilityAlertDetailsPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AlertDetailsPage/AlertDetailsPage.tsx
@@ -58,11 +58,11 @@ import {
} from '../../utils/RouterUtils';
import { getEntityIcon } from '../../utils/TableUtils';
import '../AddObservabilityPage/add-observability-page.less';
-import { ObservabilityAlertDetailsPageProps } from './ObservabilityAlertDetailsPage.interface';
+import { AlertDetailsPageProps } from './AlertDetailsPage.interface';
-function ObservabilityAlertDetailsPage({
- isNotificationAlert,
-}: Readonly) {
+function AlertDetailsPage({
+ isNotificationAlert = false,
+}: Readonly) {
const { t } = useTranslation();
const { fqn } = useFqn();
const history = useHistory();
@@ -441,4 +441,4 @@ function ObservabilityAlertDetailsPage({
);
}
-export default ObservabilityAlertDetailsPage;
+export default AlertDetailsPage;
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/NotificationListPage/NotificationListPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/NotificationListPage/NotificationListPage.test.tsx
index 030fe811a57..f93acea3d0b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/NotificationListPage/NotificationListPage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/NotificationListPage/NotificationListPage.test.tsx
@@ -10,10 +10,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { render } from '@testing-library/react';
+import { act, render, screen } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ROUTES } from '../../constants/constants';
+import { getAllAlerts } from '../../rest/alertsAPI';
import NotificationListPage from './NotificationListPage';
const MOCK_DATA = [
@@ -55,6 +56,12 @@ jest.mock('../../rest/alertsAPI', () => ({
paging: { total: 1 },
})
),
+ getAlertsFromName: jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ name: 'ActivityFeedAlert',
+ params: 'all',
+ })
+ ),
}));
jest.mock('../../utils/GlobalSettingsUtils', () => ({
@@ -77,28 +84,68 @@ jest.mock(
}
);
-describe('Alerts Page Tests', () => {
+describe('Notification Alerts Page Tests', () => {
it('Title should be rendered', async () => {
- const { findByText } = render(, {
- wrapper: MemoryRouter,
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
});
- expect(await findByText('label.notification-plural')).toBeInTheDocument();
+ expect(
+ await screen.findByText('label.notification-plural')
+ ).toBeInTheDocument();
});
it('SubTitle should be rendered', async () => {
- const { findByText } = render(, {
- wrapper: MemoryRouter,
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
});
- expect(await findByText(/message.alerts-description/)).toBeInTheDocument();
+ expect(
+ await screen.findByText(/message.alerts-description/)
+ ).toBeInTheDocument();
});
it('Add alert button should be rendered', async () => {
- const { findByText } = render(, {
- wrapper: MemoryRouter,
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
});
- expect(await findByText(/label.add-entity/)).toBeInTheDocument();
+ expect(await screen.findByText(/label.add-entity/)).toBeInTheDocument();
+ });
+
+ it('Table should render alerts data', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ const alertNameElement = await screen.findByTestId('alert-name');
+
+ expect(alertNameElement).toBeInTheDocument();
+ });
+
+ it('Table should render no data', async () => {
+ (getAllAlerts as jest.Mock).mockImplementation(() =>
+ Promise.resolve({
+ data: [],
+ paging: { total: 1 },
+ })
+ );
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ const alertNameElement = await screen.findByText('label.no-entity');
+
+ expect(alertNameElement).toBeInTheDocument();
});
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertsPage/ObservabilityAlertsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertsPage/ObservabilityAlertsPage.test.tsx
index 09cd050943b..cb8bb57c3ad 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertsPage/ObservabilityAlertsPage.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/ObservabilityAlertsPage/ObservabilityAlertsPage.test.tsx
@@ -10,9 +10,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { render } from '@testing-library/react';
+import { act, render, screen } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
+import { getAllAlerts } from '../../rest/alertsAPI';
import ObservabilityAlertsPage from './ObservabilityAlertsPage';
const MOCK_DATA = [
@@ -55,29 +56,72 @@ jest.mock('../../rest/alertsAPI', () => ({
})
),
}));
+jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => {
+ return jest.fn().mockImplementation(({ children }) => {children}
);
+});
-describe.skip('Alerts Page Tests', () => {
+describe('Observability Alerts Page Tests', () => {
it('Title should be rendered', async () => {
- const { findByText } = render(, {
- wrapper: MemoryRouter,
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
});
- expect(await findByText('label.alert-plural')).toBeInTheDocument();
+ expect(await screen.findByText('label.observability')).toBeInTheDocument();
});
it('SubTitle should be rendered', async () => {
- const { findByText } = render(, {
- wrapper: MemoryRouter,
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
});
- expect(await findByText(/message.alerts-description/)).toBeInTheDocument();
+ expect(
+ await screen.findByText(/message.alerts-description/)
+ ).toBeInTheDocument();
});
it('Add alert button should be rendered', async () => {
- const { findByText } = render(, {
- wrapper: MemoryRouter,
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
});
- expect(await findByText(/label.create-entity/)).toBeInTheDocument();
+ expect(await screen.findByText(/label.add-entity/)).toBeInTheDocument();
+ });
+
+ it('Table should render alerts data', async () => {
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ const alertNameElement = await screen.findByText('alert-test');
+
+ expect(alertNameElement).toBeInTheDocument();
+ });
+
+ it('Table should render no data', async () => {
+ (getAllAlerts as jest.Mock).mockImplementation(() =>
+ Promise.resolve({
+ data: [],
+ paging: { total: 1 },
+ })
+ );
+ await act(async () => {
+ render(, {
+ wrapper: MemoryRouter,
+ });
+ });
+
+ const alertNameElement = await screen.findByText(
+ 'message.adding-new-entity-is-easy-just-give-it-a-spin'
+ );
+
+ expect(alertNameElement).toBeInTheDocument();
});
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/observability.mock.ts b/openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/observability.mock.ts
new file mode 100644
index 00000000000..9496409246f
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/observability.mock.ts
@@ -0,0 +1,343 @@
+/*
+ * 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 { FilterResourceDescriptor } from '../../../generated/events/filterResourceDescriptor';
+
+export const MOCK_FILTER_RESOURCES = [
+ {
+ name: 'container',
+ supportedFilters: [
+ {
+ name: 'filterByFqn',
+ displayName: 'Container Name',
+ fullyQualifiedName: 'eventSubscription.filterByFqn',
+ description: 'Event Filtering By Container Name',
+ effect: 'include',
+ condition: 'matchAnyEntityFqn(${fqnList})',
+ arguments: ['fqnList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByDomain',
+ displayName: 'Domain',
+ fullyQualifiedName: 'eventSubscription.filterByDomain',
+ description: 'Event Filtering By Domain a Entity Belongs To',
+ effect: 'include',
+ condition: 'matchAnyDomain(${domainList})',
+ arguments: ['domainList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByOwner',
+ displayName: 'Owner Name',
+ fullyQualifiedName: 'eventSubscription.filterByOwner',
+ description: 'Event Filtering By Owner Name of Asset',
+ effect: 'include',
+ condition: 'matchAnyOwnerName(${ownerNameList})',
+ arguments: ['ownerNameList'],
+ inputType: 'runtime',
+ },
+ ],
+ supportedActions: [
+ {
+ name: 'GetContainerSchemaChanges',
+ displayName: 'Get Schema Changes',
+ fullyQualifiedName: 'eventSubscription.GetContainerSchemaChanges',
+ description: 'Get Updates for Schema Changes',
+ effect: 'include',
+ condition: "matchAnyFieldChange({'parent','children'})",
+ arguments: [],
+ inputType: 'none',
+ },
+ ],
+ },
+ {
+ name: 'pipeline',
+ supportedFilters: [
+ {
+ name: 'filterByFqn',
+ displayName: 'Pipeline Name',
+ fullyQualifiedName: 'eventSubscription.filterByFqn',
+ description: 'Event Filtering By Pipeline Name',
+ effect: 'include',
+ condition: 'matchAnyEntityFqn(${fqnList})',
+ arguments: ['fqnList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByDomain',
+ displayName: 'Domain',
+ fullyQualifiedName: 'eventSubscription.filterByDomain',
+ description: 'Event Filtering By Domain a Entity Belongs To',
+ effect: 'include',
+ condition: 'matchAnyDomain(${domainList})',
+ arguments: ['domainList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByOwner',
+ displayName: 'Owner Name',
+ fullyQualifiedName: 'eventSubscription.filterByOwner',
+ description: 'Event Filtering By Owner Name of Asset',
+ effect: 'include',
+ condition: 'matchAnyOwnerName(${ownerNameList})',
+ arguments: ['ownerNameList'],
+ inputType: 'runtime',
+ },
+ ],
+ supportedActions: [
+ {
+ name: 'GetPipelineStatusUpdates',
+ displayName: 'Get Pipeline Status Updates',
+ fullyQualifiedName: 'eventSubscription.GetPipelineStatusUpdates',
+ description: 'Get Updates for Pipeline Status Changes',
+ effect: 'include',
+ condition: 'matchIngestionPipelineState(${pipelineStateList})',
+ arguments: ['pipelineStateList'],
+ inputType: 'runtime',
+ },
+ ],
+ },
+ {
+ name: 'table',
+ supportedFilters: [
+ {
+ name: 'filterByFqn',
+ displayName: 'Table Name',
+ fullyQualifiedName: 'eventSubscription.filterByFqn',
+ description: 'Event Filtering By Table Name',
+ effect: 'include',
+ condition: 'matchAnyEntityFqn(${fqnList})',
+ arguments: ['fqnList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByDomain',
+ displayName: 'Domain',
+ fullyQualifiedName: 'eventSubscription.filterByDomain',
+ description: 'Event Filtering By Domain a Entity Belongs To',
+ effect: 'include',
+ condition: 'matchAnyDomain(${domainList})',
+ arguments: ['domainList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByOwner',
+ displayName: 'Owner Name',
+ fullyQualifiedName: 'eventSubscription.filterByOwner',
+ description: 'Event Filtering By Owner Name of Asset',
+ effect: 'include',
+ condition: 'matchAnyOwnerName(${ownerNameList})',
+ arguments: ['ownerNameList'],
+ inputType: 'runtime',
+ },
+ ],
+ supportedActions: [
+ {
+ name: 'GetTableSchemaChanges',
+ displayName: 'Get Schema Changes',
+ fullyQualifiedName: 'eventSubscription.GetTableSchemaChanges',
+ description: 'Get Updates for Schema Changes',
+ effect: 'include',
+ condition: "matchAnyFieldChange({'columns','dataModel','joins'})",
+ arguments: [],
+ inputType: 'none',
+ },
+ {
+ name: 'GetTableMetricsUpdates',
+ displayName: 'Get Table Metrics Updates',
+ fullyQualifiedName: 'eventSubscription.GetTableMetricsUpdates',
+ description: 'Get Updates About Table Metrics',
+ effect: 'include',
+ condition: "matchAnyFieldChange({'customMetrics', 'profile'})",
+ arguments: [],
+ inputType: 'none',
+ },
+ ],
+ },
+ {
+ name: 'testCase',
+ supportedFilters: [
+ {
+ name: 'filterByFqn',
+ displayName: 'Test Case Name',
+ fullyQualifiedName: 'eventSubscription.filterByFqn',
+ description: 'Event By Test Case Name',
+ effect: 'include',
+ condition: 'matchAnyEntityFqn(${fqnList})',
+ arguments: ['fqnList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByDomain',
+ displayName: 'Domain',
+ fullyQualifiedName: 'eventSubscription.filterByDomain',
+ description: 'Event Filtering By Domain a Entity Belongs To',
+ effect: 'include',
+ condition: 'matchAnyDomain(${domainList})',
+ arguments: ['domainList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByOwner',
+ displayName: 'Owner Name',
+ fullyQualifiedName: 'eventSubscription.filterByOwner',
+ description: 'Event Filtering By Owner Name of Asset',
+ effect: 'include',
+ condition: 'matchAnyOwnerName(${ownerNameList})',
+ arguments: ['ownerNameList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByTableNameTestCaseBelongsTo',
+ displayName: 'Table Name A Test Case Belongs To',
+ fullyQualifiedName:
+ 'eventSubscription.filterByTableNameTestCaseBelongsTo',
+ description: 'Event Filtering By Table Name A Test Case Belongs To',
+ effect: 'include',
+ condition: 'filterByTableNameTestCaseBelongsTo(${tableNameList})',
+ arguments: ['tableNameList'],
+ inputType: 'runtime',
+ },
+ ],
+ supportedActions: [
+ {
+ name: 'GetTestCaseSchemaChanges',
+ displayName: 'Get Schema Changes',
+ fullyQualifiedName: 'eventSubscription.filterBySchemaChange',
+ description: 'Get Updates for Schema Changes',
+ effect: 'include',
+ condition:
+ "matchAnyFieldChange({'testDefinition','parameterValues','description'})",
+ arguments: [],
+ inputType: 'none',
+ },
+ {
+ name: 'GetTestCaseStatusUpdates',
+ displayName: 'Get Test Case Status Updates',
+ fullyQualifiedName: 'eventSubscription.GetTestCaseStatusUpdates',
+ description: 'Get Status Updates Test Cases',
+ effect: 'include',
+ condition: 'matchTestResult(${testResultList})',
+ arguments: ['testResultList'],
+ inputType: 'runtime',
+ },
+ ],
+ },
+ {
+ name: 'testSuite',
+ supportedFilters: [
+ {
+ name: 'filterByFqn',
+ displayName: 'Test Suite Name',
+ fullyQualifiedName: 'eventSubscription.filterByFqn',
+ description: 'Event Filtering By Test Suite Name',
+ effect: 'include',
+ condition: 'matchAnyEntityFqn(${fqnList})',
+ arguments: ['fqnList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByOwner',
+ displayName: 'Owner Name',
+ fullyQualifiedName: 'eventSubscription.filterByOwner',
+ description: 'Event Filtering By Owner Name of Asset',
+ effect: 'include',
+ condition: 'matchAnyOwnerName(${ownerNameList})',
+ arguments: ['ownerNameList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByDomain',
+ displayName: 'Domain',
+ fullyQualifiedName: 'eventSubscription.filterByDomain',
+ description: 'Event Filtering By Domain a Entity Belongs To',
+ effect: 'include',
+ condition: 'matchAnyDomain(${domainList})',
+ arguments: ['domainList'],
+ inputType: 'runtime',
+ },
+ ],
+ supportedActions: [
+ {
+ name: 'GetTestSuiteSchemaChanges',
+ displayName: 'Get Schema Changes',
+ fullyQualifiedName: 'eventSubscription.filterBySchemaChange',
+ description: 'Get Updates for Schema Changes',
+ effect: 'include',
+ condition:
+ "matchAnyFieldChange({'connection','pipelines','description'})",
+ arguments: [],
+ inputType: 'none',
+ },
+ {
+ name: 'GetTestCaseStatusUpdatesUnderSuite',
+ displayName: 'Get Test Case Status Updates belonging to a Test Suite',
+ fullyQualifiedName:
+ 'eventSubscription.GetTestCaseStatusUpdatesUnderSuite',
+ description: 'Get Status Updates Test Cases belonging to a Test Suite',
+ effect: 'include',
+ condition:
+ 'getTestCaseStatusIfInTestSuite(${testSuiteList}, ${testStatusList})',
+ arguments: ['testSuiteList', 'testStatusList'],
+ inputType: 'runtime',
+ },
+ ],
+ },
+ {
+ name: 'topic',
+ supportedFilters: [
+ {
+ name: 'filterByFqn',
+ displayName: 'Topic Name',
+ fullyQualifiedName: 'eventSubscription.filterByFqn',
+ description: 'Event Filtering By Topic Name',
+ effect: 'include',
+ condition: 'matchAnyEntityFqn(${fqnList})',
+ arguments: ['fqnList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByDomain',
+ displayName: 'Domain',
+ fullyQualifiedName: 'eventSubscription.filterByDomain',
+ description: 'Event Filtering By Domain a Entity Belongs To',
+ effect: 'include',
+ condition: 'matchAnyDomain(${domainList})',
+ arguments: ['domainList'],
+ inputType: 'runtime',
+ },
+ {
+ name: 'filterByOwner',
+ displayName: 'Owner Name',
+ fullyQualifiedName: 'eventSubscription.filterByOwner',
+ description: 'Event Filtering By Owner Name of Asset',
+ effect: 'include',
+ condition: 'matchAnyOwnerName(${ownerNameList})',
+ arguments: ['ownerNameList'],
+ inputType: 'runtime',
+ },
+ ],
+ supportedActions: [
+ {
+ name: 'GetTopicSchemaChanges',
+ displayName: 'Get Schema Changes',
+ fullyQualifiedName: 'eventSubscription.GetTopicSchemaChanges',
+ description: 'Get Updates for Schema Changes',
+ effect: 'include',
+ condition: "matchAnyFieldChange({'messageSchema'})",
+ arguments: [],
+ inputType: 'none',
+ },
+ ],
+ },
+] as FilterResourceDescriptor[];
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.test.tsx
index 744745c56cb..f21e0b3c730 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.test.tsx
@@ -10,8 +10,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-import { getFunctionDisplayName } from './AlertsUtil';
+import React from 'react';
+import { ReactComponent as AllActivityIcon } from '../../assets/svg/all-activity.svg';
+import { ReactComponent as MailIcon } from '../../assets/svg/ic-mail.svg';
+import { ReactComponent as MSTeamsIcon } from '../../assets/svg/ms-teams.svg';
+import { ReactComponent as SlackIcon } from '../../assets/svg/slack.svg';
+import { ReactComponent as WebhookIcon } from '../../assets/svg/webhook.svg';
+import { SubscriptionType } from '../../generated/events/eventSubscription';
+import {
+ getAlertActionTypeDisplayName,
+ getAlertsActionTypeIcon,
+ getDisplayNameForEntities,
+ getFunctionDisplayName,
+ listLengthValidator,
+} from './AlertsUtil';
describe('AlertsUtil tests', () => {
it('getFunctionDisplayName should return correct text for matchAnyEntityFqn', () => {
@@ -47,4 +59,83 @@ describe('AlertsUtil tests', () => {
'label.entity-id-match'
);
});
+
+ it('getAlertsActionTypeIcon should return correct icon for Slack', () => {
+ const icon = getAlertsActionTypeIcon(SubscriptionType.Slack);
+
+ expect(icon).toStrictEqual();
+ });
+
+ it('getAlertsActionTypeIcon should return correct icon for Email', () => {
+ const icon = getAlertsActionTypeIcon(SubscriptionType.Email);
+
+ expect(icon).toStrictEqual();
+ });
+
+ it('getAlertsActionTypeIcon should return correct icon for MSTeam', () => {
+ const icon = getAlertsActionTypeIcon(SubscriptionType.MSTeams);
+
+ expect(icon).toStrictEqual();
+ });
+
+ it('getAlertsActionTypeIcon should return correct icon for ActivityFeed', () => {
+ const icon = getAlertsActionTypeIcon(SubscriptionType.ActivityFeed);
+
+ expect(icon).toStrictEqual();
+ });
+
+ it('getAlertsActionTypeIcon should return correct icon for generic', () => {
+ const icon = getAlertsActionTypeIcon(SubscriptionType.Generic);
+
+ expect(icon).toStrictEqual();
+ });
+
+ it('listLengthValidator should return error function', async () => {
+ const error = listLengthValidator('name', 64);
+
+ expect(typeof error).toBe('function');
+ });
+
+ it('getAlertActionTypeDisplayName should return correct text for Slack', () => {
+ expect(getAlertActionTypeDisplayName(SubscriptionType.Slack)).toBe(
+ 'label.slack'
+ );
+ });
+
+ it('getAlertActionTypeDisplayName should return correct text for Email', () => {
+ expect(getAlertActionTypeDisplayName(SubscriptionType.Email)).toBe(
+ 'label.email'
+ );
+ });
+
+ it('getAlertActionTypeDisplayName should return correct text for MSTeam', () => {
+ expect(getAlertActionTypeDisplayName(SubscriptionType.MSTeams)).toBe(
+ 'label.ms-team-plural'
+ );
+ });
+
+ it('getAlertActionTypeDisplayName should return correct text for ActivityFeed', () => {
+ expect(getAlertActionTypeDisplayName(SubscriptionType.ActivityFeed)).toBe(
+ 'label.activity-feed-plural'
+ );
+ });
+
+ it('getAlertActionTypeDisplayName should return correct text for generic', () => {
+ expect(getAlertActionTypeDisplayName(SubscriptionType.Generic)).toBe(
+ 'label.webhook'
+ );
+ });
+
+ it('getAlertActionTypeDisplayName should return correct text for GChat', () => {
+ expect(getAlertActionTypeDisplayName(SubscriptionType.GChat)).toBe(
+ 'label.g-chat'
+ );
+ });
+
+ it('getDisplayNameForEntities should return correct text', () => {
+ expect(getDisplayNameForEntities('kpi')).toBe('label.kpi-uppercase');
+ expect(getDisplayNameForEntities('mlmodel')).toBe('label.ml-model');
+
+ expect(getDisplayNameForEntities('unknown')).toBe('Unknown');
+ });
});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.tsx
index 20cf063976e..198641bb962 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/Alerts/AlertsUtil.tsx
@@ -11,7 +11,7 @@
* limitations under the License.
*/
-import { Col, Input, Select, Switch, Tooltip, Typography } from 'antd';
+import { Col, Input, Select, Switch, Tooltip } from 'antd';
import Form, { RuleObject } from 'antd/lib/form';
import { AxiosError } from 'axios';
import i18next, { t } from 'i18next';
@@ -91,24 +91,6 @@ export const getFunctionDisplayName = (func: string): string => {
}
};
-export const StyledCard = ({
- heading,
- subHeading,
-}: {
- heading: string;
- subHeading: string;
-}) => {
- return (
-
- {heading}
-
-
- {subHeading}
-
-
- );
-};
-
/**
*
* @param name Field name used to identify which field has error
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityUtils.test.ts
new file mode 100644
index 00000000000..9a56d3419ca
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/ObservabilityUtils.test.ts
@@ -0,0 +1,90 @@
+/*
+ * 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 { SubscriptionCategory } from '../generated/events/eventSubscription';
+import {
+ checkIfDestinationIsInternal,
+ getAlertDestinationCategoryIcons,
+ getConfigFieldFromDestinationType,
+} from './ObservabilityUtils';
+
+describe('Observability Utils test', () => {
+ describe('getAlertDestinationCategoryIcons', () => {
+ it('should return the correct icon for each type', () => {
+ const types = [
+ 'Teams',
+ 'Users',
+ 'Admins',
+ 'GChat',
+ 'Slack',
+ 'Email',
+ 'MsTeams',
+ 'Followers',
+ 'Generic',
+ 'Owners',
+ ];
+ types.forEach((type) => {
+ const icon = getAlertDestinationCategoryIcons(type);
+
+ expect(icon).not.toBeNull();
+ });
+ });
+
+ it('should return null for an unknown type', () => {
+ const icon = getAlertDestinationCategoryIcons('Unknown');
+
+ expect(icon).toBeNull();
+ });
+ });
+
+ describe('checkIfDestinationIsInternal', () => {
+ it('should return true for internal destinations', () => {
+ const destinationName = SubscriptionCategory.Admins;
+ const result = checkIfDestinationIsInternal(destinationName);
+
+ expect(result).toBe(true);
+ });
+
+ it('should return false for external destinations', () => {
+ const destinationName = 'Test';
+ const result = checkIfDestinationIsInternal(destinationName);
+
+ expect(result).toBe(false);
+ });
+ });
+
+ describe('getConfigFieldFromDestinationType', () => {
+ it('should return the correct config field for each type', () => {
+ const types = [
+ SubscriptionCategory.Admins,
+ SubscriptionCategory.Owners,
+ SubscriptionCategory.Followers,
+ ];
+ const expectedResults = [
+ 'sendToAdmins',
+ 'sendToOwners',
+ 'sendToFollowers',
+ ];
+ types.forEach((type, index) => {
+ const result = getConfigFieldFromDestinationType(type);
+
+ expect(result).toBe(expectedResults[index]);
+ });
+ });
+
+ it('should return an empty string for an unknown type', () => {
+ const result = getConfigFieldFromDestinationType('Unknown');
+
+ expect(result).toBe('');
+ });
+ });
+});