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 () => {