diff --git a/x-pack/test/functional/services/observability/alerts.ts b/x-pack/test/functional/services/observability/alerts/common.ts similarity index 95% rename from x-pack/test/functional/services/observability/alerts.ts rename to x-pack/test/functional/services/observability/alerts/common.ts index 435da8ad94037..7098fdec2a9d4 100644 --- a/x-pack/test/functional/services/observability/alerts.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -7,8 +7,8 @@ import querystring from 'querystring'; import { chunk } from 'lodash'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { WebElementWrapper } from '../../../../../../test/functional/services/lib/web_element_wrapper'; // Based on the x-pack/test/functional/es_archives/observability/alerts archive. const DATE_WITH_DATA = { @@ -19,12 +19,14 @@ const DATE_WITH_DATA = { const ALERTS_FLYOUT_SELECTOR = 'alertsFlyout'; const COPY_TO_CLIPBOARD_BUTTON_SELECTOR = 'copy-to-clipboard'; const ALERTS_TABLE_CONTAINER_SELECTOR = 'events-viewer-panel'; - const ACTION_COLUMN_INDEX = 1; type WorkflowStatus = 'open' | 'acknowledged' | 'closed'; -export function ObservabilityAlertsProvider({ getPageObjects, getService }: FtrProviderContext) { +export function ObservabilityAlertsCommonProvider({ + getPageObjects, + getService, +}: FtrProviderContext) { const testSubjects = getService('testSubjects'); const flyoutService = getService('flyout'); const pageObjects = getPageObjects(['common']); @@ -156,6 +158,7 @@ export function ObservabilityAlertsProvider({ getPageObjects, getService }: FtrP await actionsOverflowButton.click(); }; + // Workflow status const setWorkflowStatusForRow = async (rowIndex: number, workflowStatus: WorkflowStatus) => { await openActionsMenuForRow(rowIndex); diff --git a/x-pack/test/functional/services/observability/alerts/index.ts b/x-pack/test/functional/services/observability/alerts/index.ts new file mode 100644 index 0000000000000..f373b0d75c543 --- /dev/null +++ b/x-pack/test/functional/services/observability/alerts/index.ts @@ -0,0 +1,21 @@ +/* + * 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 { ObservabilityAlertsPaginationProvider } from './pagination'; +import { ObservabilityAlertsCommonProvider } from './common'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export function ObservabilityAlertsProvider(context: FtrProviderContext) { + const common = ObservabilityAlertsCommonProvider(context); + const pagination = ObservabilityAlertsPaginationProvider(context); + + return { + common, + pagination, + }; +} diff --git a/x-pack/test/functional/services/observability/alerts/pagination.ts b/x-pack/test/functional/services/observability/alerts/pagination.ts new file mode 100644 index 0000000000000..6bffcf3596e2d --- /dev/null +++ b/x-pack/test/functional/services/observability/alerts/pagination.ts @@ -0,0 +1,109 @@ +/* + * 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +const ROWS_PER_PAGE_SELECTOR = 'tablePaginationPopoverButton'; +const PREV_BUTTON_SELECTOR = 'pagination-button-previous'; +const NEXT_BUTTON_SELECTOR = 'pagination-button-next'; +const TEN_ROWS_SELECTOR = 'tablePagination-10-rows'; +const TWENTY_FIVE_ROWS_SELECTOR = 'tablePagination-25-rows'; +const FIFTY_ROWS_SELECTOR = 'tablePagination-50-rows'; +const BUTTON_ONE_SELECTOR = 'pagination-button-0'; +const BUTTON_TWO_SELECTOR = 'pagination-button-1'; + +export function ObservabilityAlertsPaginationProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + const getPageSizeSelector = async () => { + return await testSubjects.find(ROWS_PER_PAGE_SELECTOR); + }; + + const getPageSizeSelectorOrFail = async () => { + return await testSubjects.existOrFail(ROWS_PER_PAGE_SELECTOR); + }; + + const missingPageSizeSelectorOrFail = async () => { + return await testSubjects.missingOrFail(ROWS_PER_PAGE_SELECTOR); + }; + + const getTenRowsPageSelector = async () => { + return await testSubjects.find(TEN_ROWS_SELECTOR); + }; + + const getTwentyFiveRowsPageSelector = async () => { + return await testSubjects.find(TWENTY_FIVE_ROWS_SELECTOR); + }; + + const getFiftyRowsPageSelector = async () => { + return await testSubjects.find(FIFTY_ROWS_SELECTOR); + }; + + const getPrevPageButton = async () => { + return await testSubjects.find(PREV_BUTTON_SELECTOR); + }; + + const getPrevPageButtonOrFail = async () => { + return await testSubjects.existOrFail(PREV_BUTTON_SELECTOR); + }; + + const missingPrevPageButtonOrFail = async () => { + return await testSubjects.missingOrFail(PREV_BUTTON_SELECTOR); + }; + + const getNextPageButton = async () => { + return await testSubjects.find(NEXT_BUTTON_SELECTOR); + }; + + const getNextPageButtonOrFail = async () => { + return await testSubjects.existOrFail(NEXT_BUTTON_SELECTOR); + }; + + const getPaginationButtonOne = async () => { + return await testSubjects.find(BUTTON_ONE_SELECTOR); + }; + + const getPaginationButtonTwo = async () => { + return await testSubjects.find(BUTTON_TWO_SELECTOR); + }; + + const goToNextPage = async () => { + return await (await getNextPageButton()).click(); + }; + + const goToPrevPage = async () => { + return await (await getPrevPageButton()).click(); + }; + + const goToFirstPage = async () => { + await (await getPaginationButtonOne()).click(); + }; + + const getPrevButtonDisabledValue = async () => { + return await (await getPrevPageButton()).getAttribute('disabled'); + }; + + return { + getPageSizeSelector, + getPageSizeSelectorOrFail, + missingPageSizeSelectorOrFail, + getTenRowsPageSelector, + getTwentyFiveRowsPageSelector, + getFiftyRowsPageSelector, + getPrevPageButton, + getPrevPageButtonOrFail, + missingPrevPageButtonOrFail, + getNextPageButton, + getNextPageButtonOrFail, + getPaginationButtonOne, + getPaginationButtonTwo, + goToNextPage, + goToPrevPage, + goToFirstPage, + getPrevButtonDisabledValue, + }; +} diff --git a/x-pack/test/observability_functional/apps/observability/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/alerts/index.ts index 856d7e60996ec..14019472eb2ca 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/index.ts @@ -31,7 +31,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); - await observability.alerts.navigateToTimeWithData(); + await observability.alerts.common.navigateToTimeWithData(); }); after(async () => { @@ -40,50 +40,50 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Alerts table', () => { it('Renders the table', async () => { - await observability.alerts.getTableOrFail(); + await observability.alerts.common.getTableOrFail(); }); it('Renders the correct number of cells', async () => { await retry.try(async () => { - const cells = await observability.alerts.getTableCells(); + const cells = await observability.alerts.common.getTableCells(); expect(cells.length).to.be(TOTAL_ALERTS_CELL_COUNT); }); }); describe('Filtering', () => { afterEach(async () => { - await observability.alerts.clearQueryBar(); + await observability.alerts.common.clearQueryBar(); }); after(async () => { // NOTE: We do this as the query bar takes the place of the datepicker when it is in focus, so we'll reset // back to default. - await observability.alerts.submitQuery(''); + await observability.alerts.common.submitQuery(''); }); it('Autocompletion works', async () => { - await observability.alerts.typeInQueryBar('kibana.alert.s'); + await observability.alerts.common.typeInQueryBar('kibana.alert.s'); await testSubjects.existOrFail('autocompleteSuggestion-field-kibana.alert.start-'); await testSubjects.existOrFail('autocompleteSuggestion-field-kibana.alert.status-'); }); it('Applies filters correctly', async () => { - await observability.alerts.submitQuery('kibana.alert.status: recovered'); + await observability.alerts.common.submitQuery('kibana.alert.status: recovered'); await retry.try(async () => { - const cells = await observability.alerts.getTableCells(); + const cells = await observability.alerts.common.getTableCells(); expect(cells.length).to.be(RECOVERED_ALERTS_CELL_COUNT); }); }); it('Displays a no data state when filters produce zero results', async () => { - await observability.alerts.submitQuery('kibana.alert.consumer: uptime'); - await observability.alerts.getNoDataStateOrFail(); + await observability.alerts.common.submitQuery('kibana.alert.consumer: uptime'); + await observability.alerts.common.getNoDataStateOrFail(); }); }); describe('Date selection', () => { after(async () => { - await observability.alerts.navigateToTimeWithData(); + await observability.alerts.common.navigateToTimeWithData(); }); it('Correctly applies date picker selections', async () => { @@ -91,7 +91,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await (await testSubjects.find('superDatePickerToggleQuickMenuButton')).click(); // We shouldn't expect any data for the last 15 minutes await (await testSubjects.find('superDatePickerCommonlyUsed_Last_15 minutes')).click(); - await observability.alerts.getNoDataStateOrFail(); + await observability.alerts.common.getNoDataStateOrFail(); await pageObjects.common.waitUntilUrlIncludes('rangeFrom=now-15m&rangeTo=now'); }); }); @@ -99,37 +99,38 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Flyout', () => { it('Can be opened', async () => { - await observability.alerts.openAlertsFlyout(); - await observability.alerts.getAlertsFlyoutOrFail(); + await observability.alerts.common.openAlertsFlyout(); + await observability.alerts.common.getAlertsFlyoutOrFail(); }); it('Can be closed', async () => { - await observability.alerts.closeAlertsFlyout(); + await observability.alerts.common.closeAlertsFlyout(); await testSubjects.missingOrFail('alertsFlyout'); }); describe('When open', async () => { before(async () => { - await observability.alerts.openAlertsFlyout(); + await observability.alerts.common.openAlertsFlyout(); }); after(async () => { - await observability.alerts.closeAlertsFlyout(); + await observability.alerts.common.closeAlertsFlyout(); }); it('Displays the correct title', async () => { await retry.try(async () => { const titleText = await ( - await observability.alerts.getAlertsFlyoutTitle() + await observability.alerts.common.getAlertsFlyoutTitle() ).getVisibleText(); expect(titleText).to.contain('Log threshold'); }); }); it('Displays the correct content', async () => { - const flyoutTitles = await observability.alerts.getAlertsFlyoutDescriptionListTitles(); + const flyoutTitles = + await observability.alerts.common.getAlertsFlyoutDescriptionListTitles(); const flyoutDescriptions = - await observability.alerts.getAlertsFlyoutDescriptionListDescriptions(); + await observability.alerts.common.getAlertsFlyoutDescriptionListDescriptions(); const expectedTitles = [ 'Status', @@ -158,7 +159,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('Displays a View in App button', async () => { - await observability.alerts.getAlertsFlyoutViewInAppButtonOrFail(); + await observability.alerts.common.getAlertsFlyoutViewInAppButtonOrFail(); }); }); }); @@ -166,35 +167,35 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Cell actions', () => { beforeEach(async () => { await retry.try(async () => { - const cells = await observability.alerts.getTableCells(); + const cells = await observability.alerts.common.getTableCells(); const alertStatusCell = cells[2]; await alertStatusCell.moveMouseTo(); await retry.waitFor( 'cell actions visible', - async () => await observability.alerts.copyToClipboardButtonExists() + async () => await observability.alerts.common.copyToClipboardButtonExists() ); }); }); afterEach(async () => { - await observability.alerts.clearQueryBar(); + await observability.alerts.common.clearQueryBar(); }); it('Copy button works', async () => { // NOTE: We don't have access to the clipboard in a headless environment, // so we'll just check the button is clickable in the functional tests. - await (await observability.alerts.getCopyToClipboardButton()).click(); + await (await observability.alerts.common.getCopyToClipboardButton()).click(); }); it('Filter for value works', async () => { - await (await observability.alerts.getFilterForValueButton()).click(); + await (await observability.alerts.common.getFilterForValueButton()).click(); const queryBarValue = await ( - await observability.alerts.getQueryBar() + await observability.alerts.common.getQueryBar() ).getAttribute('value'); expect(queryBarValue).to.be('kibana.alert.status: "active"'); // Wait for request await retry.try(async () => { - const cells = await observability.alerts.getTableCells(); + const cells = await observability.alerts.common.getTableCells(); expect(cells.length).to.be(ACTIVE_ALERTS_CELL_COUNT); }); }); diff --git a/x-pack/test/observability_functional/apps/observability/alerts/pagination.ts b/x-pack/test/observability_functional/apps/observability/alerts/pagination.ts new file mode 100644 index 0000000000000..5cefe8fd42c8a --- /dev/null +++ b/x-pack/test/observability_functional/apps/observability/alerts/pagination.ts @@ -0,0 +1,129 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const ROWS_NEEDED_FOR_PAGINATION = 10; +const DEFAULT_ROWS_PER_PAGE = 50; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + + describe('Observability alerts pagination', function () { + this.tags('includeFirefox'); + + const retry = getService('retry'); + const observability = getService('observability'); + + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await observability.alerts.common.navigateToTimeWithData(); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + describe(`When less than ${ROWS_NEEDED_FOR_PAGINATION} alerts are found`, () => { + before(async () => { + // current archiver has 3 closed alerts + await observability.alerts.common.setWorkflowStatusFilter('closed'); + }); + + after(async () => { + await observability.alerts.common.setWorkflowStatusFilter('open'); + }); + + it('Does not render page size selector', async () => { + await observability.alerts.pagination.missingPageSizeSelectorOrFail(); + }); + + it('Does not render pagination controls', async () => { + await observability.alerts.pagination.missingPrevPageButtonOrFail(); + }); + }); + + describe(`When ${ROWS_NEEDED_FOR_PAGINATION} alerts are found`, () => { + before(async () => { + // current archiver has 12 open alerts + await observability.alerts.common.setWorkflowStatusFilter('open'); + }); + + describe('Page size selector', () => { + it('Renders page size selector', async () => { + await observability.alerts.pagination.getPageSizeSelectorOrFail(); + }); + + it('Default rows per page is 50', async () => { + await retry.try(async () => { + const defaultAlertsPerPage = await ( + await observability.alerts.pagination.getPageSizeSelector() + ).getVisibleText(); + expect(defaultAlertsPerPage).to.contain(DEFAULT_ROWS_PER_PAGE); + }); + }); + + it('Shows up to 10 rows per page', async () => { + await retry.try(async () => { + await (await observability.alerts.pagination.getPageSizeSelector()).click(); + await (await observability.alerts.pagination.getTenRowsPageSelector()).click(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); + expect(tableRows.length).to.not.be.greaterThan(10); + }); + }); + + it('Shows up to 25 rows per page', async () => { + await retry.try(async () => { + await (await observability.alerts.pagination.getPageSizeSelector()).click(); + await (await observability.alerts.pagination.getTwentyFiveRowsPageSelector()).click(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); + expect(tableRows.length).to.not.be.greaterThan(25); + }); + }); + }); + + describe('Pagination controls', () => { + before(async () => { + await (await observability.alerts.pagination.getPageSizeSelector()).click(); + await (await observability.alerts.pagination.getTenRowsPageSelector()).click(); + }); + beforeEach(async () => { + await observability.alerts.pagination.goToFirstPage(); + }); + + it('Renders previous page button', async () => { + await observability.alerts.pagination.getPrevPageButtonOrFail(); + }); + + it('Renders next page button', async () => { + await observability.alerts.pagination.getNextPageButtonOrFail(); + }); + + it('Previous page button is disabled', async () => { + const prevButtonDisabledValue = + await observability.alerts.pagination.getPrevButtonDisabledValue(); + expect(prevButtonDisabledValue).to.be('true'); + }); + + it('Goes to next page', async () => { + await observability.alerts.pagination.goToNextPage(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); + expect(tableRows.length).to.be(2); + }); + + it('Goes to previous page', async () => { + await (await observability.alerts.pagination.getPaginationButtonTwo()).click(); + await observability.alerts.pagination.goToPrevPage(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); + + expect(tableRows.length).to.be(10); + }); + }); + }); + }); +}; diff --git a/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts b/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts index d491e239c6035..a68636b8cb0c0 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts @@ -8,6 +8,8 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; +const OPEN_ALERTS_ROWS_COUNT = 12; + export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); @@ -19,7 +21,7 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); - await observability.alerts.navigateToTimeWithData(); + await observability.alerts.common.navigateToTimeWithData(); }); after(async () => { @@ -28,61 +30,61 @@ export default ({ getService }: FtrProviderContext) => { it('is filtered to only show "open" alerts by default', async () => { await retry.try(async () => { - const tableRows = await observability.alerts.getTableCellsInRows(); - expect(tableRows.length).to.be(12); + const tableRows = await observability.alerts.common.getTableCellsInRows(); + expect(tableRows.length).to.be(OPEN_ALERTS_ROWS_COUNT); }); }); it('can be set to "acknowledged" using the row menu', async () => { - await observability.alerts.setWorkflowStatusForRow(0, 'acknowledged'); + await observability.alerts.common.setWorkflowStatusForRow(0, 'acknowledged'); await retry.try(async () => { - const tableRows = await observability.alerts.getTableCellsInRows(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); expect(tableRows.length).to.be(11); }); }); it('can be filtered to only show "acknowledged" alerts using the filter button', async () => { - await observability.alerts.setWorkflowStatusFilter('acknowledged'); + await observability.alerts.common.setWorkflowStatusFilter('acknowledged'); await retry.try(async () => { - const tableRows = await observability.alerts.getTableCellsInRows(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); expect(tableRows.length).to.be(3); }); }); it('can be set to "closed" using the row menu', async () => { - await observability.alerts.setWorkflowStatusForRow(0, 'closed'); + await observability.alerts.common.setWorkflowStatusForRow(0, 'closed'); await retry.try(async () => { - const tableRows = await observability.alerts.getTableCellsInRows(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); expect(tableRows.length).to.be(2); }); }); it('can be filtered to only show "closed" alerts using the filter button', async () => { - await observability.alerts.setWorkflowStatusFilter('closed'); + await observability.alerts.common.setWorkflowStatusFilter('closed'); await retry.try(async () => { - const tableRows = await observability.alerts.getTableCellsInRows(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); expect(tableRows.length).to.be(4); }); }); it('can be set to "open" using the row menu', async () => { - await observability.alerts.setWorkflowStatusForRow(0, 'open'); + await observability.alerts.common.setWorkflowStatusForRow(0, 'open'); await retry.try(async () => { - const tableRows = await observability.alerts.getTableCellsInRows(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); expect(tableRows.length).to.be(3); }); }); it('can be filtered to only show "open" alerts using the filter button', async () => { - await observability.alerts.setWorkflowStatusFilter('open'); + await observability.alerts.common.setWorkflowStatusFilter('open'); await retry.try(async () => { - const tableRows = await observability.alerts.getTableCellsInRows(); + const tableRows = await observability.alerts.common.getTableCellsInRows(); expect(tableRows.length).to.be(12); }); }); diff --git a/x-pack/test/observability_functional/apps/observability/index.ts b/x-pack/test/observability_functional/apps/observability/index.ts index b823e1ee0869b..019fb0994715e 100644 --- a/x-pack/test/observability_functional/apps/observability/index.ts +++ b/x-pack/test/observability_functional/apps/observability/index.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./alerts')); loadTestFile(require.resolve('./alerts/workflow_status')); + loadTestFile(require.resolve('./alerts/pagination')); }); }