diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 10924aaa18785..8d217c6dc9e11 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -90,10 +90,13 @@ disabled: - x-pack/test_serverless/functional/test_suites/common/config.ts - x-pack/test_serverless/functional/test_suites/observability/config.ts - x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts + - x-pack/test_serverless/functional/test_suites/observability/config.examples.ts - x-pack/test_serverless/functional/test_suites/search/config.ts - x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts + - x-pack/test_serverless/functional/test_suites/search/config.examples.ts - x-pack/test_serverless/functional/test_suites/security/config.ts - x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts + - x-pack/test_serverless/functional/test_suites/security/config.examples.ts defaultQueue: 'n2-4-spot' enabled: diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index ed0b4f6e18868..7a4291fab7003 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -69,6 +69,19 @@ steps: - exit_status: '-1' limit: 3 + - command: SERVERLESS_ENVIRONMENT=observability.examples .buildkite/scripts/steps/functional/serverless_ftr.sh + label: 'Serverless Observability Examples Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 40 + soft_fail: + - exit_status: 10 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - command: SERVERLESS_ENVIRONMENT=search .buildkite/scripts/steps/functional/serverless_ftr.sh label: 'Serverless Search Tests' agents: @@ -82,6 +95,19 @@ steps: - exit_status: '-1' limit: 3 + - command: SERVERLESS_ENVIRONMENT=search.examples .buildkite/scripts/steps/functional/serverless_ftr.sh + label: 'Serverless Search Examples Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 40 + soft_fail: + - exit_status: 10 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - command: SERVERLESS_ENVIRONMENT=security .buildkite/scripts/steps/functional/serverless_ftr.sh label: 'Serverless Security Tests' agents: @@ -95,6 +121,19 @@ steps: - exit_status: '-1' limit: 3 + - command: SERVERLESS_ENVIRONMENT=security.examples .buildkite/scripts/steps/functional/serverless_ftr.sh + label: 'Serverless Security Examples Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 40 + soft_fail: + - exit_status: 10 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - command: .buildkite/scripts/steps/functional/security_serverless.sh label: 'Serverless Security Cypress Tests' agents: diff --git a/.buildkite/pipelines/serverless.yml b/.buildkite/pipelines/serverless.yml index f78905d383ba9..be9816545e2bf 100644 --- a/.buildkite/pipelines/serverless.yml +++ b/.buildkite/pipelines/serverless.yml @@ -34,6 +34,19 @@ steps: - exit_status: '*' limit: 1 + - command: SERVERLESS_ENVIRONMENT=observability.examples .buildkite/scripts/steps/functional/serverless_ftr.sh + label: 'Serverless Observability Examples Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 40 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + - command: SERVERLESS_ENVIRONMENT=search .buildkite/scripts/steps/functional/serverless_ftr.sh label: 'Serverless Search Tests' agents: @@ -47,6 +60,19 @@ steps: - exit_status: '*' limit: 1 + - command: SERVERLESS_ENVIRONMENT=search.examples .buildkite/scripts/steps/functional/serverless_ftr.sh + label: 'Serverless Search Examples Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 40 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + - command: SERVERLESS_ENVIRONMENT=security .buildkite/scripts/steps/functional/serverless_ftr.sh label: 'Serverless Security Tests' agents: @@ -60,6 +86,19 @@ steps: - exit_status: '*' limit: 1 + - command: SERVERLESS_ENVIRONMENT=security.examples .buildkite/scripts/steps/functional/serverless_ftr.sh + label: 'Serverless Security Examples Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 40 + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 + - command: .buildkite/scripts/steps/functional/security_serverless.sh label: 'Serverless Security Cypress Tests' agents: diff --git a/.buildkite/scripts/steps/functional/serverless_ftr.sh b/.buildkite/scripts/steps/functional/serverless_ftr.sh index 72a69897e54e3..335b4b97f1445 100755 --- a/.buildkite/scripts/steps/functional/serverless_ftr.sh +++ b/.buildkite/scripts/steps/functional/serverless_ftr.sh @@ -12,6 +12,10 @@ if [[ "$SERVERLESS_ENVIRONMENT" == "search" ]]; then "x-pack/test_serverless/api_integration/test_suites/search/config.feature_flags.ts" "x-pack/test_serverless/functional/test_suites/search/config.ts" ) +elif [[ "$SERVERLESS_ENVIRONMENT" == "search.examples" ]]; then + SERVERLESS_CONFIGS=( + "x-pack/test_serverless/functional/test_suites/search/config.examples.ts" + ) elif [[ "$SERVERLESS_ENVIRONMENT" == "observability" ]]; then SERVERLESS_CONFIGS=( "x-pack/test_serverless/api_integration/test_suites/observability/config.ts" @@ -19,12 +23,20 @@ elif [[ "$SERVERLESS_ENVIRONMENT" == "observability" ]]; then "x-pack/test_serverless/functional/test_suites/observability/config.ts" "x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts" ) +elif [[ "$SERVERLESS_ENVIRONMENT" == "observability.examples" ]]; then + SERVERLESS_CONFIGS=( + "x-pack/test_serverless/functional/test_suites/observability/config.examples.ts" + ) elif [[ "$SERVERLESS_ENVIRONMENT" == "security" ]]; then SERVERLESS_CONFIGS=( "x-pack/test_serverless/api_integration/test_suites/security/config.ts" "x-pack/test_serverless/api_integration/test_suites/security/config.feature_flags.ts" "x-pack/test_serverless/functional/test_suites/security/config.ts" ) +elif [[ "$SERVERLESS_ENVIRONMENT" == "security.examples" ]]; then + SERVERLESS_CONFIGS=( + "x-pack/test_serverless/functional/test_suites/security/config.examples.ts" + ) fi EXIT_CODE=0 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4f050e3bf422f..628972c59311c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -832,6 +832,13 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations /x-pack/test_serverless/api_integration/test_suites/common/scripts_tests @elastic/kibana-data-discovery /x-pack/test_serverless/api_integration/test_suites/common/search_oss @elastic/kibana-data-discovery /x-pack/test_serverless/api_integration/test_suites/common/search_xpack @elastic/kibana-data-discovery +/x-pack/test_serverless/functional/test_suites/common/examples/data_view_field_editor_example @elastic/kibana-data-discovery +/x-pack/test_serverless/functional/test_suites/common/examples/discover_customization_examples @elastic/kibana-data-discovery +/x-pack/test_serverless/functional/test_suites/common/examples/field_formats @elastic/kibana-data-discovery +/x-pack/test_serverless/functional/test_suites/common/examples/partial_results @elastic/kibana-data-discovery +/x-pack/test_serverless/functional/test_suites/common/examples/search @elastic/kibana-data-discovery +/x-pack/test_serverless/functional/test_suites/common/examples/search_examples @elastic/kibana-data-discovery +/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples @elastic/kibana-data-discovery # Visualizations /src/plugins/visualize/ @elastic/kibana-visualizations diff --git a/x-pack/test_serverless/functional/config.base.ts b/x-pack/test_serverless/functional/config.base.ts index 640ae2402b544..4106a2b55e302 100644 --- a/x-pack/test_serverless/functional/config.base.ts +++ b/x-pack/test_serverless/functional/config.base.ts @@ -27,6 +27,7 @@ export function createTestConfig(options: CreateTestConfigOptions) { serverArgs: [ ...svlSharedConfig.get('kbnTestServer.serverArgs'), `--serverless=${options.serverlessProject}`, + ...(options.kbnServerArgs ?? []), ], }, testFiles: options.testFiles, diff --git a/x-pack/test_serverless/functional/es_archives/pre_calculated_histogram/data.json b/x-pack/test_serverless/functional/es_archives/pre_calculated_histogram/data.json new file mode 100644 index 0000000000000..129ba80854225 --- /dev/null +++ b/x-pack/test_serverless/functional/es_archives/pre_calculated_histogram/data.json @@ -0,0 +1,231 @@ +{ + "type": "doc", + "value": { + "id": "index-pattern:histogram-test", + "index": ".kibana_analytics_1", + "source": { + "index-pattern": { + "fieldAttrs": "{}", + "title": "histogram-test", + "sourceFilters": "[]", + "fields": "[]", + "fieldFormatMap": "{}", + "typeMeta": "{}", + "runtimeFieldMap": "{}", + "name": "histogram-test" + }, + "type": "index-pattern", + "references": [], + "managed": false, + "namespaces": [ + "default" + ], + "coreMigrationVersion": "8.8.0", + "typeMigrationVersion": "8.0.0", + "updated_at": "2023-08-05T05:41:10.360Z", + "created_at": "2023-08-05T05:41:10.360Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e69404d93193e4074f0ec1a", + "index": "histogram-test", + "source": { + "histogram-title": "incididunt reprehenderit mollit", + "histogram-content": { + "values": [ + 0.3, + 1, + 3, + 4.2, + 4.8 + ], + "counts": [ + 237, + 170, + 33, + 149, + 241 + ] + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e69408f2fc61f57fd5bc762", + "index": "histogram-test", + "source": { + "histogram-title": "culpa cillum ullamco", + "histogram-content": { + "values": [ + 0.5, + 1, + 1.2, + 1.3, + 2.8, + 3.9, + 4.3 + ], + "counts": [ + 113, + 197, + 20, + 66, + 20, + 39, + 178 + ] + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e6940b979b57ad343114cc3", + "index": "histogram-test", + "source": { + "histogram-title": "enim veniam et", + "histogram-content": { + "values": [ + 3.7, + 4.2 + ], + "counts": [ + 227, + 141 + ] + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e6940d3e95de786eeb7586d", + "index": "histogram-test", + "source": { + "histogram-title": "est incididunt sunt", + "histogram-content": { + "values": [ + 1.8, + 2.4, + 2.6, + 4.9 + ], + "counts": [ + 92, + 101, + 122, + 244 + ] + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e694119fb2f956a822b93b9", + "index": "histogram-test", + "source": { + "histogram-title": "qui qui tempor", + "histogram-content": { + "values": [ + 0.5, + 2.1, + 2.7, + 3, + 3.2, + 3.5, + 4.2, + 5 + ], + "counts": [ + 210, + 168, + 182, + 181, + 97, + 164, + 77, + 2 + ] + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e694145ad3c741aa12d6e8e", + "index": "histogram-test", + "source": { + "histogram-title": "ullamco nisi sunt", + "histogram-content": { + "values": [ + 1.7, + 4.5, + 4.8 + ], + "counts": [ + 74, + 146, + 141 + ] + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e694159d909d9d99b5e12d1", + "index": "histogram-test", + "source": { + "histogram-title": "magna eu incididunt", + "histogram-content": { + "values": [ + 1, + 3.4, + 4.8 + ], + "counts": [ + 103, + 205, + 11 + ] + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5e694159d909d9d99b5e12d1", + "index": "histogram-test", + "source": { + "histogram-title": "single value", + "histogram-content": { + "values": [ + 1 + ], + "counts": [ + 1 + ] + } + } + } +} diff --git a/x-pack/test_serverless/functional/es_archives/pre_calculated_histogram/mappings.json b/x-pack/test_serverless/functional/es_archives/pre_calculated_histogram/mappings.json new file mode 100644 index 0000000000000..70373f1dadbef --- /dev/null +++ b/x-pack/test_serverless/functional/es_archives/pre_calculated_histogram/mappings.json @@ -0,0 +1,30 @@ +{ + "type": "index", + "value": { + "aliases": {}, + "index": "histogram-test", + "mappings": { + "properties": { + "histogram-title": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "histogram-content": { + "type": "histogram" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/data_view_field_editor_example/data_view_field_editor_example.ts b/x-pack/test_serverless/functional/test_suites/common/examples/data_view_field_editor_example/data_view_field_editor_example.ts new file mode 100644 index 0000000000000..cd8e22e095543 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/data_view_field_editor_example/data_view_field_editor_example.ts @@ -0,0 +1,45 @@ +/* + * 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'; +// TODO: Changed from PluginFunctionalProviderContext to FtrProviderContext in Serverless +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const find = getService('find'); + + describe('', () => { + it('finds a data view', async () => { + await testSubjects.existOrFail('dataViewTitle'); + }); + + it('opens the field editor', async () => { + await testSubjects.click('addField'); + await testSubjects.existOrFail('flyoutTitle'); + await testSubjects.click('closeFlyoutButton'); + }); + + it('uses preconfigured options for a new field', async () => { + // find the checkbox label and click it - `testSubjects.setCheckbox()` is not working for our checkbox + const controlWrapper = await testSubjects.find('preconfiguredControlWrapper'); + const control = await find.descendantDisplayedByCssSelector('label', controlWrapper); + await control.click(); + + await testSubjects.click('addField'); + await testSubjects.existOrFail('flyoutTitle'); + + const nameField = await testSubjects.find('nameField'); + const nameInput = await find.descendantDisplayedByCssSelector( + '[data-test-subj=input]', + nameField + ); + + expect(await nameInput.getAttribute('value')).to.equal('demotestfield'); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/data_view_field_editor_example/index.ts b/x-pack/test_serverless/functional/test_suites/common/examples/data_view_field_editor_example/index.ts new file mode 100644 index 0000000000000..5e3ab0cd27057 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/data_view_field_editor_example/index.ts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +// TODO: Changed from PluginFunctionalProviderContext to FtrProviderContext in Serverless +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const es = getService('es'); + const PageObjects = getPageObjects(['common', 'header', 'settings', 'svlCommonNavigation']); + const testSubjects = getService('testSubjects'); + const find = getService('find'); + const retry = getService('retry'); + const kibanaServer = getService('kibanaServer'); + + describe('data view field editor example', function () { + before(async () => { + // TODO: emptyKibanaIndex fails in Serverless with + // "index_not_found_exception: no such index [.kibana_ingest]", + // so it was switched to `savedObjects.cleanStandardList()` + await kibanaServer.savedObjects.cleanStandardList(); + await browser.setWindowSize(1300, 900); + await es.transport.request({ + path: '/blogs/_doc', + method: 'POST', + body: { user: 'matt', message: 20 }, + }); + + // TODO: Navigation to Data View Management is different in Serverless + await PageObjects.common.navigateToApp('management'); + await retry.waitFor('data views link', async () => { + if (await testSubjects.exists('app-card-dataViews')) { + await testSubjects.click('app-card-dataViews'); + return true; + } + if (await find.existsByCssSelector('[href*="/dataViews"]')) { + await find.clickByCssSelector('[href*="/dataViews"]'); + return true; + } + return false; + }); + await PageObjects.settings.createIndexPattern('blogs', null); + await PageObjects.common.navigateToApp('dataViewFieldEditorExample'); + }); + + loadTestFile(require.resolve('./data_view_field_editor_example')); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/discover_customization_examples/customizations.ts b/x-pack/test_serverless/functional/test_suites/common/examples/discover_customization_examples/customizations.ts new file mode 100644 index 0000000000000..7b97c1256ce6d --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/discover_customization_examples/customizations.ts @@ -0,0 +1,99 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +const TEST_START_TIME = 'Sep 19, 2015 @ 06:31:44.000'; +const TEST_END_TIME = 'Sep 23, 2015 @ 18:31:44.000'; + +export default ({ getService, getPageObjects }: FtrProviderContext) => { + const PageObjects = getPageObjects(['common', 'timePicker', 'header']); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const dataGrid = getService('dataGrid'); + const defaultSettings = { defaultIndex: 'logstash-*' }; + + describe('Customizations', () => { + before(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + await kibanaServer.uiSettings.replace(defaultSettings); + await PageObjects.common.navigateToApp('home'); + const currentUrl = await browser.getCurrentUrl(); + const customizationUrl = + currentUrl.substring(0, currentUrl.indexOf('/app/home')) + + '/app/discoverCustomizationExamples'; + await browser.get(customizationUrl); + await PageObjects.timePicker.setAbsoluteRange(TEST_START_TIME, TEST_END_TIME); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + after(async () => { + await kibanaServer.uiSettings.unset('defaultIndex'); + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); + await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.savedObjects.cleanStandardList(); + }); + + it('Top nav', async () => { + await testSubjects.existOrFail('customOptionsButton'); + await testSubjects.existOrFail('shareTopNavButton'); + await testSubjects.existOrFail('documentExplorerButton'); + await testSubjects.missingOrFail('discoverNewButton'); + await testSubjects.missingOrFail('discoverOpenButton'); + await testSubjects.click('customOptionsButton'); + await testSubjects.existOrFail('customOptionsPopover'); + await testSubjects.click('customOptionsButton'); + await testSubjects.missingOrFail('customOptionsPopover'); + }); + + it('Search bar', async () => { + await testSubjects.click('logsViewSelectorButton'); + await testSubjects.click('logsViewSelectorOption-ASavedSearch'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const { title, description } = await PageObjects.common.getSharedItemTitleAndDescription(); + const expected = { + title: 'A Saved Search', + description: 'A Saved Search Description', + }; + expect(title).to.eql(expected.title); + expect(description).to.eql(expected.description); + }); + + it('Search bar Prepend Filters exists and should apply filter properly', async () => { + // Validate custom filters are present + await testSubjects.existOrFail('customPrependedFilter'); + await testSubjects.click('customPrependedFilter'); + await testSubjects.existOrFail('optionsList-control-selection-exists'); + + // Retrieve option list popover + const optionsListControl = await testSubjects.find('optionsList-control-popover'); + const optionsItems = await optionsListControl.findAllByCssSelector( + '[data-test-subj*="optionsList-control-selection-"]' + ); + + // Retrieve second item in the options along with the count of documents + const item = optionsItems[1]; + const countBadge = await item.findByCssSelector( + '[data-test-subj="optionsList-document-count-badge"]' + ); + const documentsCount = parseInt(await countBadge.getVisibleText(), 10); + + // Click the item to apply filter + await item.click(); + await PageObjects.header.waitUntilLoadingHasFinished(); + + // Validate that filter is applied + const rows = await dataGrid.getDocTableRows(); + await expect(documentsCount).to.eql(rows.length); + }); + }); +}; diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/discover_customization_examples/index.ts b/x-pack/test_serverless/functional/test_suites/common/examples/discover_customization_examples/index.ts new file mode 100644 index 0000000000000..ab89a5e4ec994 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/discover_customization_examples/index.ts @@ -0,0 +1,14 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Discover customization examples', () => { + loadTestFile(require.resolve('./customizations')); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/field_formats/index.ts b/x-pack/test_serverless/functional/test_suites/common/examples/field_formats/index.ts new file mode 100644 index 0000000000000..8bd85620e9857 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/field_formats/index.ts @@ -0,0 +1,38 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + + describe('Field formats example', function () { + before(async () => { + await PageObjects.common.navigateToApp('fieldFormatsExample'); + }); + + it('renders field formats example 1', async () => { + const formattedValues = await Promise.all( + ( + await testSubjects.findAll('example1 sample formatted') + ).map((wrapper) => wrapper.getVisibleText()) + ); + expect(formattedValues).to.eql(['1000.00B', '97.66KB', '95.37MB']); + }); + + it('renders field formats example 2', async () => { + const formattedValues = await Promise.all( + ( + await testSubjects.findAll('example2 sample formatted') + ).map((wrapper) => wrapper.getVisibleText()) + ); + expect(formattedValues).to.eql(['$1,000.00', '$100,000.00', '$100,000,000.00']); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/partial_results/index.ts b/x-pack/test_serverless/functional/test_suites/common/examples/partial_results/index.ts new file mode 100644 index 0000000000000..fa0036812672d --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/partial_results/index.ts @@ -0,0 +1,44 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + + describe('Partial Results Example', function () { + before(async () => { + await PageObjects.common.navigateToApp('partialResultsExample'); + + const element = await testSubjects.find('example-help'); + + await element.click(); + await element.click(); + await element.click(); + }); + + it('should trace mouse events', async () => { + const events = await Promise.all( + ( + await testSubjects.findAll('example-column-event') + ).map((wrapper) => wrapper.getVisibleText()) + ); + expect(events).to.eql(['mousedown', 'mouseup', 'click']); + }); + + it('should keep track of the events number', async () => { + const counters = await Promise.all( + ( + await testSubjects.findAll('example-column-count') + ).map((wrapper) => wrapper.getVisibleText()) + ); + expect(counters).to.eql(['3', '3', '3']); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/search/index.ts b/x-pack/test_serverless/functional/test_suites/common/examples/search/index.ts new file mode 100644 index 0000000000000..c9997f5f41ce9 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/search/index.ts @@ -0,0 +1,14 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Search examples', () => { + loadTestFile(require.resolve('./warnings')); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/search/warnings.ts b/x-pack/test_serverless/functional/test_suites/common/examples/search/warnings.ts new file mode 100644 index 0000000000000..026fa9f2efd25 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/search/warnings.ts @@ -0,0 +1,204 @@ +/* + * 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 type { estypes } from '@elastic/elasticsearch'; +import expect from '@kbn/expect'; +import { asyncForEach } from '@kbn/std'; +import assert from 'assert'; +import type { WebElementWrapper } from '../../../../../../../test/functional/services/lib/web_element_wrapper'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'timePicker']); + const testSubjects = getService('testSubjects'); + const find = getService('find'); + const retry = getService('retry'); + const es = getService('es'); + const log = getService('log'); + const indexPatterns = getService('indexPatterns'); + const comboBox = getService('comboBox'); + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('handling warnings with search source fetch', function () { + const dataViewTitle = 'sample-01,sample-01-rollup'; + const fromTime = 'Jun 17, 2022 @ 00:00:00.000'; + const toTime = 'Jun 23, 2022 @ 00:00:00.000'; + const testArchive = 'test/functional/fixtures/es_archiver/search/downsampled'; + const testIndex = 'sample-01'; + const testRollupIndex = 'sample-01-rollup'; + const testRollupField = 'kubernetes.container.memory.usage.bytes'; + const toastsSelector = '[data-test-subj=globalToastList] [data-test-subj=euiToastHeader]'; + const shardFailureType = 'unsupported_aggregation_on_downsampled_index'; + const shardFailureReason = `Field [${testRollupField}] of type [aggregate_metric_double] is not supported for aggregation [percentiles]`; + + const getTestJson = async (tabTestSubj: string, codeTestSubj: string) => { + log.info(`switch to ${tabTestSubj} tab...`); + await testSubjects.click(tabTestSubj); + const block = await testSubjects.find(codeTestSubj); + const testText = (await block.getVisibleText()).trim(); + return testText && JSON.parse(testText); + }; + + before(async () => { + // create rollup data + log.info(`loading ${testIndex} index...`); + await esArchiver.loadIfNeeded(testArchive); + log.info(`add write block to ${testIndex} index...`); + await es.indices.addBlock({ index: testIndex, block: 'write' }); + try { + log.info(`rolling up ${testIndex} index...`); + // es client currently does not have method for downsample + await es.transport.request({ + method: 'POST', + path: '/sample-01/_downsample/sample-01-rollup', + body: { fixed_interval: '1h' }, + }); + } catch (err) { + log.info(`ignoring resource_already_exists_exception...`); + if (!err.message.match(/resource_already_exists_exception/)) { + throw err; + } + } + + log.info(`creating ${dataViewTitle} data view...`); + await indexPatterns.create( + { + title: dataViewTitle, + timeFieldName: '@timestamp', + }, + { override: true } + ); + await kibanaServer.uiSettings.update({ + 'dateFormat:tz': 'UTC', + defaultIndex: '0ae0bc7a-e4ca-405c-ab67-f2b5913f2a51', + 'timepicker:timeDefaults': '{ "from": "now-1y", "to": "now" }', + }); + + await PageObjects.common.navigateToApp('searchExamples'); + }); + + beforeEach(async () => { + await comboBox.setCustom('dataViewSelector', dataViewTitle); + await comboBox.set('searchMetricField', testRollupField); + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }); + + after(async () => { + await es.indices.delete({ index: [testIndex, testRollupIndex] }); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.replace({}); + }); + + afterEach(async () => { + await PageObjects.common.clearAllToasts(); + }); + + it('shows shard failure warning notifications by default', async () => { + await testSubjects.click('searchSourceWithOther'); + + // wait for response - toasts appear before the response is rendered + let response: estypes.SearchResponse | undefined; + await retry.try(async () => { + response = await getTestJson('responseTab', 'responseCodeBlock'); + expect(response).not.to.eql({}); + }); + + // toasts + const toasts = await find.allByCssSelector(toastsSelector); + expect(toasts.length).to.be(2); + const expects = ['2 of 4 shards failed', 'Query result']; + await asyncForEach(toasts, async (t, index) => { + expect(await t.getVisibleText()).to.eql(expects[index]); + }); + + // click "see full error" button in the toast + const [openShardModalButton] = await testSubjects.findAll('openShardFailureModalBtn'); + await openShardModalButton.click(); + const modalHeader = await testSubjects.find('shardFailureModalTitle'); + expect(await modalHeader.getVisibleText()).to.be('2 of 4 shards failed'); + // request + await testSubjects.click('shardFailuresModalRequestButton'); + const requestBlock = await testSubjects.find('shardsFailedModalRequestBlock'); + expect(await requestBlock.getVisibleText()).to.contain(testRollupField); + // response + await testSubjects.click('shardFailuresModalResponseButton'); + const responseBlock = await testSubjects.find('shardsFailedModalResponseBlock'); + expect(await responseBlock.getVisibleText()).to.contain(shardFailureReason); + + await testSubjects.click('closeShardFailureModal'); + + // response tab + assert(response && response._shards.failures); + expect(response._shards.total).to.be(4); + expect(response._shards.successful).to.be(2); + expect(response._shards.skipped).to.be(0); + expect(response._shards.failed).to.be(2); + expect(response._shards.failures.length).to.equal(1); + expect(response._shards.failures[0].index).to.equal(testRollupIndex); + expect(response._shards.failures[0].reason.type).to.equal(shardFailureType); + expect(response._shards.failures[0].reason.reason).to.equal(shardFailureReason); + + // warnings tab + const warnings = await getTestJson('warningsTab', 'warningsCodeBlock'); + expect(warnings).to.eql([]); + }); + + it('able to handle shard failure warnings and prevent default notifications', async () => { + await testSubjects.click('searchSourceWithoutOther'); + + // wait for toasts - toasts appear after the response is rendered + let toasts: WebElementWrapper[] = []; + await retry.try(async () => { + toasts = await find.allByCssSelector(toastsSelector); + expect(toasts.length).to.be(2); + }); + const expects = ['Query result', '2 of 4 shards failed']; + await asyncForEach(toasts, async (t, index) => { + expect(await t.getVisibleText()).to.eql(expects[index]); + }); + + // click "see full error" button in the toast + const [openShardModalButton] = await testSubjects.findAll('openShardFailureModalBtn'); + await openShardModalButton.click(); + const modalHeader = await testSubjects.find('shardFailureModalTitle'); + expect(await modalHeader.getVisibleText()).to.be('2 of 4 shards failed'); + // request + await testSubjects.click('shardFailuresModalRequestButton'); + const requestBlock = await testSubjects.find('shardsFailedModalRequestBlock'); + expect(await requestBlock.getVisibleText()).to.contain(testRollupField); + // response + await testSubjects.click('shardFailuresModalResponseButton'); + const responseBlock = await testSubjects.find('shardsFailedModalResponseBlock'); + expect(await responseBlock.getVisibleText()).to.contain(shardFailureReason); + + await testSubjects.click('closeShardFailureModal'); + + // response tab + const response = await getTestJson('responseTab', 'responseCodeBlock'); + expect(response._shards.total).to.be(4); + expect(response._shards.successful).to.be(2); + expect(response._shards.skipped).to.be(0); + expect(response._shards.failed).to.be(2); + expect(response._shards.failures.length).to.equal(1); + expect(response._shards.failures[0].index).to.equal(testRollupIndex); + expect(response._shards.failures[0].reason.type).to.equal(shardFailureType); + expect(response._shards.failures[0].reason.reason).to.equal(shardFailureReason); + + // warnings tab + const warnings = await getTestJson('warningsTab', 'warningsCodeBlock'); + expect(warnings).to.eql([ + { + type: 'shard_failure', + message: '2 of 4 shards failed', + reason: { reason: shardFailureReason, type: shardFailureType }, + text: 'The data might be incomplete or wrong.', + }, + ]); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/index.ts b/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/index.ts new file mode 100644 index 0000000000000..d0ea8f6131159 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/index.ts @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// TODO: Changed from PluginFunctionalProviderContext to FtrProviderContext in Serverless +import type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + describe('search examples', function () { + before(async () => { + // TODO: emptyKibanaIndex fails in Serverless with + // "index_not_found_exception: no such index [.kibana_ingest]", + // so it was switched to `savedObjects.cleanStandardList()` + await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' + ); // need at least one index pattern + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' + ); + }); + + // TODO: Removed `search_session_example` since + // search sessions are not supported in Serverless + loadTestFile(require.resolve('./search_example')); + // TODO: Removed `search_sessions_cache` since + // search sessions are not supported in Serverless + loadTestFile(require.resolve('./partial_results_example')); + // TODO: Removed `sql_search_example` since + // SQL is not supported in Serverless + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/partial_results_example.ts b/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/partial_results_example.ts new file mode 100644 index 0000000000000..93364f85cc94f --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/partial_results_example.ts @@ -0,0 +1,37 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + const retry = getService('retry'); + + describe('Partial results example', () => { + before(async () => { + await PageObjects.common.navigateToApp('searchExamples'); + await testSubjects.click('/search'); + }); + + it('should update a progress bar', async () => { + await testSubjects.click('responseTab'); + const progressBar = await testSubjects.find('progressBar'); + + const value = await progressBar.getAttribute('value'); + expect(value).to.be('0'); + + await testSubjects.click('requestFibonacci'); + + await retry.waitFor('update progress bar', async () => { + const newValue = await progressBar.getAttribute('value'); + return parseFloat(newValue) > 0; + }); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/search_example.ts b/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/search_example.ts new file mode 100644 index 0000000000000..0e5584d656c98 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/search_examples/search_example.ts @@ -0,0 +1,97 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'timePicker']); + const retry = getService('retry'); + const comboBox = getService('comboBox'); + const toasts = getService('toasts'); + + describe('Search example', () => { + describe('with bfetch', () => { + testSearchExample(); + }); + + describe('no bfetch', () => { + const kibanaServer = getService('kibanaServer'); + before(async () => { + await kibanaServer.uiSettings.replace({ + 'bfetch:disable': true, + }); + }); + after(async () => { + await kibanaServer.uiSettings.unset('bfetch:disable'); + }); + + testSearchExample(); + }); + + const appId = 'searchExamples'; + + function testSearchExample() { + before(async function () { + await PageObjects.common.navigateToApp(appId, { insertTimestamp: false }); + await comboBox.setCustom('dataViewSelector', 'logstash-*'); + await comboBox.set('searchBucketField', 'geo.src'); + await comboBox.set('searchMetricField', 'memory'); + await PageObjects.timePicker.setAbsoluteRange( + 'Mar 1, 2015 @ 00:00:00.000', + 'Nov 1, 2015 @ 00:00:00.000' + ); + }); + + beforeEach(async () => { + await toasts.dismissAllToasts(); + await retry.waitFor('toasts gone', async () => { + return (await toasts.getToastCount()) === 0; + }); + }); + + it('should have an other bucket', async () => { + await testSubjects.click('searchSourceWithOther'); + await testSubjects.click('responseTab'); + const codeBlock = await testSubjects.find('responseCodeBlock'); + await retry.waitFor('get code block', async () => { + const visibleText = await codeBlock.getVisibleText(); + const parsedResponse = JSON.parse(visibleText); + const buckets = parsedResponse.aggregations[1].buckets; + return ( + buckets.length === 3 && buckets[2].key === '__other__' && buckets[2].doc_count === 9039 + ); + }); + }); + + it('should not have an other bucket', async () => { + await testSubjects.click('searchSourceWithoutOther'); + await testSubjects.click('responseTab'); + const codeBlock = await testSubjects.find('responseCodeBlock'); + await retry.waitFor('get code block', async () => { + const visibleText = await codeBlock.getVisibleText(); + const parsedResponse = JSON.parse(visibleText); + const buckets = parsedResponse.aggregations[1].buckets; + return buckets.length === 2; + }); + }); + + it('should handle warnings', async () => { + await testSubjects.click('searchWithWarning'); + await retry.waitFor('', async () => { + const toastCount = await toasts.getToastCount(); + return toastCount > 1; + }); + const warningToast = await toasts.getToastElement(2); + const textEl = await warningToast.findByTestSubject('euiToastBody'); + const text: string = await textEl.getVisibleText(); + expect(text).to.contain('Watch out!'); + }); + } + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/existing_fields.ts b/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/existing_fields.ts new file mode 100644 index 0000000000000..184b7b5d788a0 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/existing_fields.ts @@ -0,0 +1,165 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +const TEST_START_TIME = 'Jan 2, 2021 @ 00:00:00.000'; +const TEST_END_TIME = 'Jan 2, 2022 @ 00:00:00.000'; +const metaFields = ['_id', '_index', '_score']; + +const fieldsWithData = [ + 'ts', + 'filter_field', + 'textfield1', + 'textfield2', + 'mapping_runtime_field', + 'data_view_runtime_field', +]; + +export default ({ getService, getPageObjects }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const comboBox = getService('comboBox'); + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + const monacoEditor = getService('monacoEditor'); + const PageObjects = getPageObjects(['common', 'timePicker', 'header', 'unifiedFieldList']); + const dataViewTitle = 'existence_index_*'; + + async function addDSLFilter(value: string) { + await testSubjects.click('addFilter'); + await testSubjects.click('editQueryDSL'); + await monacoEditor.waitCodeEditorReady('addFilterPopover'); + await monacoEditor.setCodeEditorValue(value); + await testSubjects.scrollIntoView('saveFilter'); + await testSubjects.clickWhenNotDisabled('saveFilter'); + await retry.try(async () => { + await testSubjects.waitForDeleted('saveFilter'); + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + } + + async function removeAllDSLFilters() { + await testSubjects.click('showQueryBarMenu'); + await testSubjects.click('filter-sets-removeAllFilters'); + + await PageObjects.header.waitUntilLoadingHasFinished(); + } + + describe('Fields existence info', () => { + before(async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword' + ); + await kibanaServer.importExport.load( + 'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json' + ); + await PageObjects.common.navigateToApp('unifiedFieldListExamples'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await retry.waitFor('combobox is ready', async () => { + return await testSubjects.exists('dataViewSelector'); + }); + await comboBox.setCustom('dataViewSelector', dataViewTitle); + await retry.waitFor('page is ready', async () => { + return await testSubjects.exists('globalQueryBar'); + }); + await PageObjects.timePicker.setAbsoluteRange(TEST_START_TIME, TEST_END_TIME); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); + await PageObjects.unifiedFieldList.toggleSidebarSection('meta'); + }); + + after(async () => { + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword' + ); + await kibanaServer.importExport.unload( + 'test/api_integration/fixtures/kbn_archiver/index_patterns/constant_keyword.json' + ); + await PageObjects.unifiedFieldList.cleanSidebarLocalStorage(); + await kibanaServer.savedObjects.cleanStandardList(); + }); + + describe('existence', () => { + it('should find which fields exist in the sample documents', async () => { + const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames(); + expect(sidebarFields.sort()).to.eql([...metaFields, ...fieldsWithData].sort()); + }); + + it('should return fields filtered by term query', async () => { + const expectedFieldNames = [ + 'ts', + 'filter_field', + 'textfield1', + // textfield2 and mapping_runtime_field are defined on the other index + 'data_view_runtime_field', + ]; + + await addDSLFilter(`{ + "bool": { + "filter": [{ "term": { "filter_field": "a" } }] + } + }`); + + const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames(); + expect(sidebarFields.sort()).to.eql([...metaFields, ...expectedFieldNames].sort()); + + await removeAllDSLFilters(); + }); + + it('should return fields filtered by match_phrase query', async () => { + const expectedFieldNames = [ + 'ts', + 'filter_field', + 'textfield1', + // textfield2 and mapping_runtime_field are defined on the other index + 'data_view_runtime_field', + ]; + + await addDSLFilter(`{ + "bool": { + "filter": [{ "match_phrase": { "filter_field": "a" } }] + } + }`); + + const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames(); + expect(sidebarFields.sort()).to.eql([...metaFields, ...expectedFieldNames].sort()); + + await removeAllDSLFilters(); + }); + + it('should return fields filtered by time range', async () => { + const expectedFieldNames = [ + 'ts', + 'filter_field', + 'textfield1', + // textfield2 and mapping_runtime_field are defined on the other index + 'data_view_runtime_field', + ]; + + await addDSLFilter(`{ + "bool": { + "filter": [{ "term": { "filter_field": "a" } }] + } + }`); + + await PageObjects.timePicker.setAbsoluteRange( + TEST_START_TIME, + 'Dec 12, 2021 @ 00:00:00.000' + ); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); + + const sidebarFields = await PageObjects.unifiedFieldList.getAllFieldNames(); + expect(sidebarFields.sort()).to.eql([...metaFields, ...expectedFieldNames].sort()); + + await removeAllDSLFilters(); + }); + }); + }); +}; diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/field_stats.ts b/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/field_stats.ts new file mode 100644 index 0000000000000..1df48eed373de --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/field_stats.ts @@ -0,0 +1,182 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +const TEST_START_TIME = 'Sep 19, 2015 @ 06:31:44.000'; +const TEST_END_TIME = 'Sep 23, 2015 @ 18:31:44.000'; + +export default ({ getService, getPageObjects }: FtrProviderContext) => { + const PageObjects = getPageObjects(['common', 'timePicker', 'header', 'unifiedFieldList']); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const comboBox = getService('comboBox'); + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + const filterBar = getService('filterBar'); + const dataViewTitle = 'logstash-2015.09.22'; + + describe('Field stats', () => { + before(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/visualize/default' + ); + // TODO: Loading this from `es_archives` in `test_serverless` + // instead since minor modifications were required + await esArchiver.loadIfNeeded( + 'x-pack/test_serverless/functional/es_archives/pre_calculated_histogram' + ); + await PageObjects.common.navigateToApp('unifiedFieldListExamples'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await retry.waitFor('combobox is ready', async () => { + return await testSubjects.exists('dataViewSelector'); + }); + await comboBox.setCustom('dataViewSelector', dataViewTitle); + await retry.waitFor('page is ready', async () => { + return await testSubjects.exists('globalQueryBar'); + }); + await PageObjects.timePicker.setAbsoluteRange(TEST_START_TIME, TEST_END_TIME); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); + // TODO: Loading this from `es_archives` in `test_serverless` + // instead since minor modifications were required + await esArchiver.unload( + 'x-pack/test_serverless/functional/es_archives/pre_calculated_histogram' + ); + await kibanaServer.savedObjects.cleanStandardList(); + await PageObjects.unifiedFieldList.cleanSidebarLocalStorage(); + }); + + describe('field distribution', () => { + before(async () => { + await PageObjects.unifiedFieldList.toggleSidebarSection('empty'); // it will allow to render more fields in Available fields section + }); + + it('should return an auto histogram for numbers and top values', async () => { + await PageObjects.unifiedFieldList.clickFieldListItem('bytes'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be( + 'topValuesAndDistribution' + ); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634); + expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be( + '0\n3.2%\n3,954\n0.1%\n5,846\n0.1%\n6,497\n0.1%\n1,840\n0.1%\n4,206\n0.1%\n4,328\n0.1%\n4,669\n0.1%\n5,863\n0.1%\n6,631\n0.1%\nOther\n96.0%' + ); + }); + + it('should return an auto histogram for dates', async () => { + await PageObjects.unifiedFieldList.clickFieldListItem('@timestamp'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be( + 'timeDistribution' + ); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634); + }); + + it('should return top values for strings', async () => { + await PageObjects.unifiedFieldList.clickFieldListItem('geo.src'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues'); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634); + expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be( + 'CN\n18.0%\nIN\n17.4%\nUS\n9.2%\nID\n3.4%\nBR\n3.1%\nPK\n2.5%\nBD\n2.3%\nNG\n2.0%\nRU\n1.8%\nJP\n1.6%\nOther\n38.8%' + ); + }); + + it('should return top values for ip fields', async () => { + await PageObjects.unifiedFieldList.clickFieldListItem('ip'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues'); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634); + expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be( + '177.194.175.66\n0.3%\n18.55.141.62\n0.3%\n53.55.251.105\n0.3%\n21.111.249.239\n0.2%\n97.63.84.25\n0.2%\n100.99.207.174\n0.2%\n112.34.138.226\n0.2%\n194.68.89.92\n0.2%\n235.186.79.201\n0.2%\n57.79.108.136\n0.2%\nOther\n97.6%' + ); + }); + + // TODO: Scripted fields tests dropped since they're not supported in Serverless + + it('should return examples for non-aggregatable or geo fields', async () => { + await PageObjects.unifiedFieldList.clickFieldListItem('geo.coordinates'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('exampleValues'); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(100); + // actual hits might vary + expect( + (await PageObjects.unifiedFieldList.getFieldStatsExampleBucketsVisibleText()).length + ).to.above(0); + }); + + it('should return top values for index pattern runtime string fields', async () => { + await PageObjects.unifiedFieldList.clickFieldListItem('runtime_string_field'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues'); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634); + expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be( + 'hello world!\n100%' + ); + }); + + it('should apply filters and queries', async () => { + await filterBar.addFilter({ field: 'geo.src', operation: 'is', value: 'US' }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedFieldList.clickFieldListItem('bytes'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be( + 'topValuesAndDistribution' + ); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(425); + await filterBar.removeFilter('geo.src'); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + it('should allow filtering on a runtime field other than the field in use', async () => { + await filterBar.addFilter({ field: 'runtime_string_field', operation: 'exists' }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedFieldList.clickFieldListItem('runtime_number_field'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('topValues'); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(4634); + expect(await PageObjects.unifiedFieldList.getFieldStatsTopValueBucketsVisibleText()).to.be( + '5\n100%' + ); + await filterBar.removeFilter('runtime_string_field'); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + }); + + describe('histogram', () => { + before(async () => { + await comboBox.setCustom('dataViewSelector', 'histogram-test'); + await retry.waitFor('page is ready', async () => { + return await testSubjects.exists('globalQueryBar'); + }); + await PageObjects.timePicker.setAbsoluteRange(TEST_START_TIME, TEST_END_TIME); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); + }); + + it('should return an auto histogram for precalculated histograms', async () => { + await PageObjects.unifiedFieldList.clickFieldListItem('histogram-content'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('histogram'); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(7); + }); + + it('should return a single-value histogram when filtering a precalculated histogram', async () => { + await filterBar.addFilter({ + field: 'histogram-title', + operation: 'is', + value: 'single value', + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.unifiedFieldList.clickFieldListItem('histogram-content'); + expect(await PageObjects.unifiedFieldList.getFieldStatsViewType()).to.be('histogram'); + expect(await PageObjects.unifiedFieldList.getFieldStatsDocsCount()).to.be(1); + await filterBar.removeFilter('histogram-title'); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + }); + }); +}; diff --git a/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/index.ts b/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/index.ts new file mode 100644 index 0000000000000..249aac6f86e0b --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/examples/unified_field_list_examples/index.ts @@ -0,0 +1,15 @@ +/* + * 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 type { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Unified Field List Examples', () => { + loadTestFile(require.resolve('./field_stats')); + loadTestFile(require.resolve('./existing_fields')); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/index.examples.ts b/x-pack/test_serverless/functional/test_suites/common/index.examples.ts new file mode 100644 index 0000000000000..58ee2e2ac4478 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/index.examples.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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('serverless examples UI', function () { + this.tags('skipMKI'); + loadTestFile(require.resolve('./examples/data_view_field_editor_example')); + loadTestFile(require.resolve('./examples/discover_customization_examples')); + loadTestFile(require.resolve('./examples/field_formats')); + loadTestFile(require.resolve('./examples/partial_results')); + loadTestFile(require.resolve('./examples/search')); + loadTestFile(require.resolve('./examples/search_examples')); + loadTestFile(require.resolve('./examples/unified_field_list_examples')); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/config.examples.ts b/x-pack/test_serverless/functional/test_suites/observability/config.examples.ts new file mode 100644 index 0000000000000..358dddbe89aca --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/config.examples.ts @@ -0,0 +1,23 @@ +/* + * 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 { REPO_ROOT } from '@kbn/repo-info'; +import { findTestPluginPaths } from '@kbn/test'; +import { resolve } from 'path'; +import { createTestConfig } from '../../config.base'; + +export default createTestConfig({ + serverlessProject: 'oblt', + testFiles: [require.resolve('../common/index.examples')], + junit: { + reportName: 'Serverless Observability Examples Functional Tests', + }, + kbnServerArgs: findTestPluginPaths([ + resolve(REPO_ROOT, 'examples'), + resolve(REPO_ROOT, 'x-pack/examples'), + ]), +}); diff --git a/x-pack/test_serverless/functional/test_suites/search/config.examples.ts b/x-pack/test_serverless/functional/test_suites/search/config.examples.ts new file mode 100644 index 0000000000000..eb7e66d8a3786 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/search/config.examples.ts @@ -0,0 +1,23 @@ +/* + * 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 { REPO_ROOT } from '@kbn/repo-info'; +import { findTestPluginPaths } from '@kbn/test'; +import { resolve } from 'path'; +import { createTestConfig } from '../../config.base'; + +export default createTestConfig({ + serverlessProject: 'es', + testFiles: [require.resolve('../common/index.examples')], + junit: { + reportName: 'Serverless Search Examples Functional Tests', + }, + kbnServerArgs: findTestPluginPaths([ + resolve(REPO_ROOT, 'examples'), + resolve(REPO_ROOT, 'x-pack/examples'), + ]), +}); diff --git a/x-pack/test_serverless/functional/test_suites/security/config.examples.ts b/x-pack/test_serverless/functional/test_suites/security/config.examples.ts new file mode 100644 index 0000000000000..7be037baa9c9a --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/security/config.examples.ts @@ -0,0 +1,23 @@ +/* + * 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 { REPO_ROOT } from '@kbn/repo-info'; +import { findTestPluginPaths } from '@kbn/test'; +import { resolve } from 'path'; +import { createTestConfig } from '../../config.base'; + +export default createTestConfig({ + serverlessProject: 'security', + testFiles: [require.resolve('../common/index.examples')], + junit: { + reportName: 'Serverless Security Examples Functional Tests', + }, + kbnServerArgs: findTestPluginPaths([ + resolve(REPO_ROOT, 'examples'), + resolve(REPO_ROOT, 'x-pack/examples'), + ]), +}); diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index c857534e8ba00..2e59d9c78a48b 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -45,6 +45,7 @@ "@kbn/cases-plugin", "@kbn/test-subj-selector", "@kbn/core-http-common", + "@kbn/std", "@kbn/data-views-plugin", "@kbn/core-saved-objects-server", "@kbn/security-api-integration-helpers",