diff --git a/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.test.tsx b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.test.tsx
new file mode 100644
index 000000000000..bdf565a29cbd
--- /dev/null
+++ b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.test.tsx
@@ -0,0 +1,130 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks';
+import React from 'react';
+import { act, waitFor } from '@testing-library/react';
+import { AlertSearchBarProps } from './types';
+import { ObservabilityAlertSearchBar } from './alert_search_bar';
+import { observabilityAlertFeatureIds } from '../../../config';
+import { useKibana } from '../../../utils/kibana_react';
+import { kibanaStartMock } from '../../../utils/kibana_react.mock';
+import { render } from '../../../utils/test_helper';
+
+const useKibanaMock = useKibana as jest.Mock;
+const getAlertsSearchBarMock = jest.fn();
+const ALERT_SEARCH_BAR_DATA_TEST_SUBJ = 'alerts-search-bar';
+const ACTIVE_BUTTON_DATA_TEST_SUBJ = 'alert-status-filter-active-button';
+
+jest.mock('../../../utils/kibana_react');
+
+const mockKibana = () => {
+ useKibanaMock.mockReturnValue({
+ services: {
+ ...kibanaStartMock.startContract().services,
+ triggersActionsUi: {
+ ...triggersActionsUiMock.createStart(),
+ getAlertsSearchBar: getAlertsSearchBarMock.mockReturnValue(
+
+ ),
+ },
+ },
+ });
+};
+
+describe('ObservabilityAlertSearchBar', () => {
+ const renderComponent = (props: Partial = {}) => {
+ const alertSearchBarProps: AlertSearchBarProps = {
+ appName: 'testAppName',
+ rangeFrom: 'now-15m',
+ setRangeFrom: jest.fn(),
+ rangeTo: 'now',
+ setRangeTo: jest.fn(),
+ kuery: '',
+ setKuery: jest.fn(),
+ status: 'active',
+ setStatus: jest.fn(),
+ setEsQuery: jest.fn(),
+ ...props,
+ };
+ return render();
+ };
+
+ beforeAll(() => {
+ mockKibana();
+ });
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render alert search bar', async () => {
+ const observabilitySearchBar = renderComponent();
+
+ await waitFor(() =>
+ expect(observabilitySearchBar.queryByTestId(ALERT_SEARCH_BAR_DATA_TEST_SUBJ)).toBeTruthy()
+ );
+ });
+
+ it('should call alert search bar with correct props', () => {
+ act(() => {
+ renderComponent();
+ });
+
+ expect(getAlertsSearchBarMock).toHaveBeenCalledWith(
+ expect.objectContaining({
+ appName: 'testAppName',
+ featureIds: observabilityAlertFeatureIds,
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ query: '',
+ }),
+ {}
+ );
+ });
+
+ it('should filter active alerts', async () => {
+ const mockedSetEsQuery = jest.fn();
+ const mockedFrom = '2022-11-15T09:38:13.604Z';
+ const mockedTo = '2022-11-15T09:53:13.604Z';
+ const { getByTestId } = renderComponent({
+ setEsQuery: mockedSetEsQuery,
+ rangeFrom: mockedFrom,
+ rangeTo: mockedTo,
+ });
+
+ await act(async () => {
+ const activeButton = getByTestId(ACTIVE_BUTTON_DATA_TEST_SUBJ);
+ activeButton.click();
+ });
+
+ expect(mockedSetEsQuery).toHaveBeenCalledWith({
+ bool: {
+ filter: [
+ {
+ bool: {
+ minimum_should_match: 1,
+ should: [{ match_phrase: { 'kibana.alert.status': 'active' } }],
+ },
+ },
+ {
+ range: {
+ '@timestamp': expect.objectContaining({
+ format: 'strict_date_optional_time',
+ gte: mockedFrom,
+ lte: mockedTo,
+ }),
+ },
+ },
+ ],
+ must: [],
+ must_not: [],
+ should: [],
+ },
+ });
+ });
+});
diff --git a/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx
index 258cc9905773..d76ebbfd4b45 100644
--- a/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx
+++ b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx
@@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { useCallback, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { Query } from '@kbn/es-query';
-import { useKibana } from '@kbn/kibana-react-plugin/public';
+import { useKibana } from '../../../utils/kibana_react';
import { observabilityAlertFeatureIds } from '../../../config';
import { ObservabilityAppServices } from '../../../application/types';
import { AlertsStatusFilter } from './components';
diff --git a/x-pack/plugins/observability/public/components/shared/alert_search_bar/types.ts b/x-pack/plugins/observability/public/components/shared/alert_search_bar/types.ts
index 7c96dac50c37..b9e3c706b18a 100644
--- a/x-pack/plugins/observability/public/components/shared/alert_search_bar/types.ts
+++ b/x-pack/plugins/observability/public/components/shared/alert_search_bar/types.ts
@@ -21,10 +21,10 @@ interface AlertSearchBarContainerState {
}
interface AlertSearchBarStateTransitions {
- setRangeFrom: (rangeFrom: string) => AlertSearchBarContainerState;
- setRangeTo: (rangeTo: string) => AlertSearchBarContainerState;
- setKuery: (kuery: string) => AlertSearchBarContainerState;
- setStatus: (status: AlertStatus) => AlertSearchBarContainerState;
+ setRangeFrom: (rangeFrom: string) => void;
+ setRangeTo: (rangeTo: string) => void;
+ setKuery: (kuery: string) => void;
+ setStatus: (status: AlertStatus) => void;
}
export interface CommonAlertSearchBarProps {
diff --git a/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts b/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts
index fe79b0650d52..31aa38cb2fd4 100644
--- a/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts
+++ b/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts
@@ -27,6 +27,7 @@ const triggersActionsUiStartMock = {
createStart() {
return {
getAddAlertFlyout: jest.fn(),
+ getAlertsSearchBar: jest.fn(),
getRuleStatusDropdown: jest.fn(),
getRuleTagBadge: jest.fn(),
getRuleStatusFilter: jest.fn(),
@@ -54,6 +55,11 @@ const data = {
dataViews: {
create: jest.fn(),
},
+ query: {
+ timefilter: {
+ timefilter: jest.fn(),
+ },
+ },
};
},
};
diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts
index 7052dcba7ff2..8c386e99423c 100644
--- a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts
+++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts
@@ -85,6 +85,13 @@ export default ({ getService }: FtrProviderContext) => {
await testSubjects.existOrFail('autocompleteSuggestion-field-kibana.alert.status-');
});
+ it('Invalid input should not break the page', async () => {
+ await observability.alerts.common.submitQuery('""""');
+ await testSubjects.existOrFail('errorToastMessage');
+ // Page should not go blank with invalid input
+ await testSubjects.existOrFail('alertsPageWithData');
+ });
+
it('Applies filters correctly', async () => {
await observability.alerts.common.submitQuery('kibana.alert.status: recovered');
await retry.try(async () => {