diff --git a/.buildkite/scout_ci_config.yml b/.buildkite/scout_ci_config.yml index 60dcde0393d66..d28bbf624b5df 100644 --- a/.buildkite/scout_ci_config.yml +++ b/.buildkite/scout_ci_config.yml @@ -4,6 +4,7 @@ plugins: - apm - console - discover_enhanced + - index_management - maps - observability - observability_onboarding diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/test_subjects.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/test_subjects.ts index f4505c4fe56b7..fa2ab6e79bb11 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/test_subjects.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/helpers/test_subjects.ts @@ -119,4 +119,9 @@ export type TestSubjects = | 'clearIndicesSearch' | 'usingMaxRetention' | 'componentTemplatesLink' + | 'summaryTabBtn' + | 'settingsTabBtn' + | 'mappingsTabBtn' + | 'aliasesTabBtn' + | 'previewTabBtn' | 'configureFailureStoreButton'; diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.helpers.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.helpers.ts index 049015ea1fb57..dbdcbac4de945 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.helpers.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.helpers.ts @@ -40,10 +40,10 @@ const createActions = (testBed: TestBed) => { const selectDetailsTab = async ( tab: 'summary' | 'settings' | 'mappings' | 'aliases' | 'preview' ) => { - const tabs = ['summary', 'settings', 'mappings', 'aliases', 'preview']; + const tabTestDataSubj = `${tab}TabBtn` as TestSubjects; await act(async () => { - testBed.find('templateDetails.tab').at(tabs.indexOf(tab)).simulate('click'); + testBed.find(tabTestDataSubj).simulate('click'); }); testBed.component.update(); }; diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.test.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.test.ts index 6647fea1cc4e2..34a56cffcb531 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.test.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/home/index_templates_tab.test.ts @@ -624,14 +624,11 @@ describe('Index Templates tab', () => { await actions.clickTemplateAt(0); - expect(find('templateDetails.tab').length).toBe(5); - expect(find('templateDetails.tab').map((t) => t.text())).toEqual([ - 'Summary', - 'Settings', - 'Mappings', - 'Aliases', - 'Preview', - ]); + expect(exists('summaryTabBtn')).toBe(true); + expect(exists('settingsTabBtn')).toBe(true); + expect(exists('mappingsTabBtn')).toBe(true); + expect(exists('aliasesTabBtn')).toBe(true); + expect(exists('previewTabBtn')).toBe(true); // Summary tab should be initial active tab expect(exists('summaryTab')).toBe(true); @@ -671,7 +668,7 @@ describe('Index Templates tab', () => { isLegacy: true, }); - const { actions, find, exists } = testBed; + const { actions, exists } = testBed; httpRequestsMockHelpers.setLoadTemplateResponse( templates[0].name, @@ -679,7 +676,6 @@ describe('Index Templates tab', () => { ); await actions.clickTemplateAt(0); - expect(find('templateDetails.tab').length).toBe(5); expect(exists('summaryTab')).toBe(true); // Navigate and verify callout message per tab diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx index e58bcbf951a17..63718bd8e4c69 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx @@ -390,21 +390,14 @@ describe('', () => { }); describe('plugin parameters', () => { - const selectMappingsEditorTab = async ( - tab: 'fields' | 'runtimeFields' | 'templates' | 'advanced' - ) => { - const tabIndex = ['fields', 'runtimeFields', 'templates', 'advanced'].indexOf(tab); - const tabElement = testBed.find('mappingsEditor.formTab').at(tabIndex); - await act(async () => { - tabElement.simulate('click'); - }); - testBed.component.update(); - }; - test('should not render the _size parameter if the mapper size plugin is not installed', async () => { const { exists } = testBed; // Navigate to the advanced configuration - await selectMappingsEditorTab('advanced'); + + await act(async () => { + testBed.find('advancedOptionsTab').simulate('click'); + }); + testBed.component.update(); expect(exists('mappingsEditor.advancedConfiguration.sizeEnabledToggle')).toBe(false); }); @@ -418,7 +411,10 @@ describe('', () => { testBed.component.update(); await navigateToMappingsStep(); - await selectMappingsEditorTab('advanced'); + await act(async () => { + testBed.find('advancedOptionsTab').simulate('click'); + }); + testBed.component.update(); expect(testBed.exists('mappingsEditor.advancedConfiguration.sizeEnabledToggle')).toBe( true diff --git a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts index 9b4ed84052d30..43d2f58a62bf6 100644 --- a/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts +++ b/x-pack/platform/plugins/shared/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts @@ -402,6 +402,6 @@ export type TestSubjects = | 'valueDataRetentionField' | 'formWizardStep-5' | 'lifecycleValue' - | 'mappingsEditor.formTab' | 'mappingsEditor.advancedConfiguration.sizeEnabledToggle' - | 'previewIndexTemplate'; + | 'previewIndexTemplate' + | 'advancedOptionsTab'; diff --git a/x-pack/platform/plugins/shared/index_management/moon.yml b/x-pack/platform/plugins/shared/index_management/moon.yml index e95accdbd32cf..ea7967d15b106 100644 --- a/x-pack/platform/plugins/shared/index_management/moon.yml +++ b/x-pack/platform/plugins/shared/index_management/moon.yml @@ -72,6 +72,7 @@ dependsOn: - '@kbn/upgrade-assistant-pkg-common' - '@kbn/failure-store-modal' - '@kbn/react-query' + - '@kbn/scout' tags: - plugin - prod diff --git a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx index 02f7fb379f552..5b47ae51ab626 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx @@ -314,8 +314,9 @@ const createActions = (testBed: TestBed) => { // --- Other --- const selectTab = async (tab: 'fields' | 'runtimeFields' | 'templates' | 'advanced') => { const index = ['fields', 'runtimeFields', 'templates', 'advanced'].indexOf(tab); + const dataTestSubjValues = ['fieldsTab', 'runtimeTab', 'templatesTab', 'advancedOptionsTab']; - const tabElement = find('formTab').at(index); + const tabElement = find(dataTestSubjValues[index]); if (tabElement.length === 0) { throw new Error(`Tab not found: "${tab}"`); } diff --git a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx index 9d165da556b19..469709b0ff454 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx @@ -166,12 +166,10 @@ describe('Mappings editor: core', () => { test('should have 4 tabs (fields, runtime, template, advanced settings)', () => { const { find } = testBed; - const tabs = find('formTab').map((wrapper) => wrapper.text()); - - expect(tabs).toEqual([ - 'Mapped fields', - 'Runtime fields', - 'Dynamic templates', + expect(find('fieldsTab').map((wrapper) => wrapper.text())).toEqual(['Mapped fields']); + expect(find('runtimeTab').map((wrapper) => wrapper.text())).toEqual(['Runtime fields']); + expect(find('templatesTab').map((wrapper) => wrapper.text())).toEqual(['Dynamic templates']); + expect(find('advancedOptionsTab').map((wrapper) => wrapper.text())).toEqual([ 'Advanced options', ]); }); diff --git a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/mappings_editor.tsx b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/mappings_editor.tsx index c1975d501969b..bf912d1fca1d4 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/mappings_editor.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/components/mappings_editor/mappings_editor.tsx @@ -184,7 +184,7 @@ export const MappingsEditor = React.memo( changeTab('fields')} isSelected={selectedTab === 'fields'} - data-test-subj="formTab" + data-test-subj="fieldsTab" > {i18n.translate('xpack.idxMgmt.mappingsEditor.fieldsTabLabel', { defaultMessage: 'Mapped fields', @@ -193,7 +193,7 @@ export const MappingsEditor = React.memo( changeTab('runtimeFields')} isSelected={selectedTab === 'runtimeFields'} - data-test-subj="formTab" + data-test-subj="runtimeTab" > {i18n.translate('xpack.idxMgmt.mappingsEditor.runtimeFieldsTabLabel', { defaultMessage: 'Runtime fields', @@ -202,7 +202,7 @@ export const MappingsEditor = React.memo( changeTab('templates')} isSelected={selectedTab === 'templates'} - data-test-subj="formTab" + data-test-subj="templatesTab" > {i18n.translate('xpack.idxMgmt.mappingsEditor.templatesTabLabel', { defaultMessage: 'Dynamic templates', @@ -211,7 +211,7 @@ export const MappingsEditor = React.memo( changeTab('advanced')} isSelected={selectedTab === 'advanced'} - data-test-subj="formTab" + data-test-subj="advancedOptionsTab" > {i18n.translate('xpack.idxMgmt.mappingsEditor.advancedTabLabel', { defaultMessage: 'Advanced options', diff --git a/x-pack/platform/plugins/shared/index_management/public/application/components/no_match/no_match.tsx b/x-pack/platform/plugins/shared/index_management/public/application/components/no_match/no_match.tsx index a96e4dce62f62..71673cdd24900 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/components/no_match/no_match.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/components/no_match/no_match.tsx @@ -94,7 +94,13 @@ export const NoMatch = ({ />

} - actions={} + actions={ + + } /> ); }; diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx index 98ea3bf6b7549..e75980a053e6a 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx @@ -55,30 +55,35 @@ const TABS = [ name: i18n.translate('xpack.idxMgmt.templateDetails.summaryTabTitle', { defaultMessage: 'Summary', }), + dataTestSubj: 'summaryTabBtn', }, { id: SETTINGS_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.settingsTabTitle', { defaultMessage: 'Settings', }), + dataTestSubj: 'settingsTabBtn', }, { id: MAPPINGS_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.mappingsTabTitle', { defaultMessage: 'Mappings', }), + dataTestSubj: 'mappingsTabBtn', }, { id: ALIASES_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.aliasesTabTitle', { defaultMessage: 'Aliases', }), + dataTestSubj: 'aliasesTabBtn', }, { id: PREVIEW_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.previewTabTitle', { defaultMessage: 'Preview', }), + dataTestSubj: 'previewTabBtn', }, ]; @@ -217,7 +222,7 @@ export const TemplateDetailsContent = ({ }} isSelected={tab.id === activeTab} key={tab.id} - data-test-subj="tab" + data-test-subj={tab.dataTestSubj} > {tab.name}
diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/custom_roles.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/custom_roles.ts new file mode 100644 index 0000000000000..b77fa5f82f7c7 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/custom_roles.ts @@ -0,0 +1,30 @@ +/* + * 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 { KibanaRole } from '@kbn/scout'; + +export const CUSTOM_ROLES: Record = { + indexManagementUser: { + elasticsearch: { + // would be nice if this wasn't needed + cluster: ['monitor', 'manage_index_templates', 'manage_enrich'], + indices: [ + { + names: ['*'], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + base: ['read'], + feature: {}, + spaces: ['*'], + }, + ], + }, +}; diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/index.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/index.ts new file mode 100644 index 0000000000000..1b7b0ab8e4348 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/index.ts @@ -0,0 +1,84 @@ +/* + * 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 { test as base } from '@kbn/scout'; +import type { + ScoutPage, + ScoutTestFixtures, + ScoutWorkerFixtures, + BrowserAuthFixture, +} from '@kbn/scout'; +import type { PageObjects } from '@kbn/scout'; +import { createLazyPageObject } from '@kbn/scout'; +import type { AbstractPageObject } from './page_objects/index_management_page'; +import { IndexManagement } from './page_objects/index_management_page'; +import { CUSTOM_ROLES } from './custom_roles'; + +type PageObjectClass = new (page: ScoutPage) => AbstractPageObject; + +export interface IndexManagementBrowserAuthFixture extends BrowserAuthFixture { + loginAsIndexManagementUser: () => Promise; +} + +export const createTest = function >( + pageObjectClassMap: Record +) { + type PageObjectsExtended = PageObjectsExtensions & PageObjects; + + function extendPOs(pageObjects: PageObjects, page: ScoutPage): PageObjectsExtended { + const initedLazyPageObjects = Object.keys(pageObjectClassMap).reduce< + Record + >((col, value) => { + col[value] = createLazyPageObject(pageObjectClassMap[value], page); + return col; + }, {}); + + return { + ...initedLazyPageObjects, + ...pageObjects, + } as PageObjectsExtended; + } + + return base.extend< + ScoutTestFixtures & { + pageObjects: PageObjectsExtended; + }, + ScoutWorkerFixtures & { + browserAuth: IndexManagementBrowserAuthFixture; + } + >({ + pageObjects: async ( + { + pageObjects, + page, + }: { + pageObjects: PageObjectsExtended; + page: ScoutPage; + }, + use: (pageObjects: PageObjectsExtended) => Promise + ) => { + const extendedPageObjects = extendPOs(pageObjects, page); + await use(extendedPageObjects); + }, + browserAuth: async ( + { browserAuth }: { browserAuth: BrowserAuthFixture }, + use: (browserAuth: IndexManagementBrowserAuthFixture) => Promise + ) => { + const loginAsIndexManagementUser = async () => + browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); + + await use({ + ...browserAuth, + loginAsIndexManagementUser, + }); + }, + }); +}; + +export const test = createTest<{ indexManagement: IndexManagement }>({ + indexManagement: IndexManagement, +}); diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index.ts new file mode 100644 index 0000000000000..ef6d312f3a37f --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index.ts @@ -0,0 +1,24 @@ +/* + * 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 { PageObjects, ScoutPage } from '@kbn/scout'; +import { createLazyPageObject } from '@kbn/scout'; +import { IndexManagement } from './index_management_page'; + +export interface IndexManagementPageObjects extends PageObjects { + indexManagement: IndexManagement; +} + +export function extendPageObjects( + pageObjects: PageObjects, + page: ScoutPage +): IndexManagementPageObjects { + return { + ...pageObjects, + indexManagement: createLazyPageObject(IndexManagement, page), + }; +} diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index_management_page.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index_management_page.ts new file mode 100644 index 0000000000000..689ee99c10a78 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index_management_page.ts @@ -0,0 +1,145 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ + +import { type ScoutPage, expect, EuiFieldTextWrapper } from '@kbn/scout'; + +export class AbstractPageObject { + constructor(public readonly page: ScoutPage) {} +} + +export class IndexManagement extends AbstractPageObject { + async goto() { + await this.page.gotoApp('management/data/index_management'); + } + + async sectionHeadingText() { + return await this.page.testSubj.locator('appTitle').textContent(); + } + + async changeTabs( + tab: + | 'indicesTab' + | 'data_streamsTab' + | 'templatesTab' + | 'component_templatesTab' + | 'enrich_policiesTab' + ) { + await this.page.testSubj.locator(tab).click(); + } + + async clickCreateIndexButton() { + await this.page.testSubj.locator('createIndexButton').click(); + } + + async setCreateIndexName(value: string) { + await this.page.testSubj.fill('createIndexNameFieldText', value); + } + + async setCreateIndexMode(value: string) { + await this.page.testSubj.click('indexModeField'); + await this.page.testSubj.locator(`indexMode${value}Option`).click(); + } + + async clickCreateIndexSaveButton() { + const saveButton = this.page.testSubj.locator('createIndexSaveButton'); + await saveButton.click(); + // Wait for modal to close using web-first assertion + await expect(saveButton).toBeHidden({ timeout: 30000 }); + } + + /** + * Returns locator for index link - caller should use web-first assertions. + * Note: Consider using extended timeout as this can be slow in CI environments. + * Example: await expect(pageObjects.indexManagement.indexLink(name)).toBeVisible({ timeout: 30000 }); + */ + indexLink(indexName: string) { + return this.page.getByRole('button').getByText(indexName); + } + + async toggleHiddenIndices() { + await this.page.testSubj.locator('checkboxToggles-includeHiddenIndices').click(); + } + + async openIndexDetailsPage(indexOfRow: number) { + const indexLinks = this.page.testSubj.locator('indexTableIndexNameLink'); + // this should be refactored to use data-test-subj on the table rows + // eslint-disable-next-line playwright/no-nth-methods + await indexLinks.nth(indexOfRow).click(); + + // Wait for index details page to load using web-first assertion + await expect(this.page.testSubj.locator('indexDetailsHeader')).toBeVisible(); + } + + async navigateToIndexManagementTab( + tab: 'indices' | 'data_streams' | 'templates' | 'component_templates' | 'enrich_policies' + ) { + await this.goto(); + const tabMap = { + indices: 'indicesTab', + data_streams: 'data_streamsTab', + templates: 'templatesTab', + component_templates: 'component_templatesTab', + enrich_policies: 'enrich_policiesTab', + }; + await this.page.testSubj.locator(tabMap[tab]).click(); + } + + async clickNextButton() { + await this.page.testSubj.locator('nextButton').click(); + } + + /** + * Custom combobox interaction for non-clearable comboboxes. + * Note: Cannot use EuiComboBoxWrapper here because the fieldType combobox + * has isClearable={false} (in type_parameter.tsx), and EuiComboBoxWrapper.selectSingleOption() + * attempts to clear() first, which fails when trying to click the non-existent comboBoxClearButton. + */ + async setComboBox(testSubject: string, value: string) { + const comboBox = this.page.testSubj.locator(testSubject); + await comboBox.click(); + + // Type the value + const input = comboBox.locator('input'); + await input.fill(value); + + // Wait for and click the option + await this.page.locator(`[role="option"]`).filter({ hasText: value }).click(); + } + + async changeMappingsEditorTab(tab: 'fields' | 'advancedOptions' | 'templates') { + const tabMap = { + fields: 'fieldsTab', + advancedOptions: 'advancedOptionsTab', + templates: 'templatesTab', + }; + await this.page.testSubj.locator(tabMap[tab]).click(); + } + + indexDetailsPage = { + expectIndexDetailsPageIsLoaded: async () => { + await expect(this.page.testSubj.locator('indexDetailsTab-overview')).toBeVisible(); + await expect(this.page.testSubj.locator('indexDetailsContent')).toBeVisible(); + await expect(this.page.testSubj.locator('indexDetailsBackToIndicesButton')).toBeVisible(); + }, + }; + + indexTemplateWizard = { + completeStepOne: async () => { + const nameField = new EuiFieldTextWrapper(this.page, { dataTestSubj: 'nameField' }); + await nameField.fill('test-index-template'); + + const indexPatternsField = new EuiFieldTextWrapper(this.page, { + dataTestSubj: 'indexPatternsField', + }); + await indexPatternsField.fill('test-index-pattern'); + + await this.clickNextButton(); + }, + }; +} diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/playwright.config.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/playwright.config.ts new file mode 100644 index 0000000000000..75a7694d12043 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/playwright.config.ts @@ -0,0 +1,12 @@ +/* + * 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 { createPlaywrightConfig } from '@kbn/scout'; + +export default createPlaywrightConfig({ + testDir: './tests', +}); diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/home_page.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/home_page.spec.ts new file mode 100644 index 0000000000000..49a02452ce3f4 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/home_page.spec.ts @@ -0,0 +1,113 @@ +/* + * 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/scout'; +import { test } from '../fixtures'; + +const testIndexName = `index-test-${Math.random()}`; + +test.describe('Home page', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ pageObjects, browserAuth }) => { + await browserAuth.loginAsIndexManagementUser(); + await pageObjects.indexManagement.goto(); + }); + + test.afterAll(async ({ esClient, log }) => { + try { + await esClient.indices.delete({ index: testIndexName }); + } catch (e: any) { + log.debug(`Index cleanup failed for ${testIndexName}: ${e.message}`); + } + }); + + test('Loads the app and renders the indices tab by default', async ({ + pageObjects, + log, + page, + }) => { + await log.debug('Checking for section heading to say Index Management.'); + + const headingText = await pageObjects.indexManagement.sectionHeadingText(); + expect(headingText).toBe('Index Management'); + + // Verify url + expect(page.url()).toContain('/indices'); + + // Verify content + await expect(page.testSubj.locator('indicesList')).toBeVisible(); + await expect(page.testSubj.locator('reloadIndicesButton')).toBeVisible(); + }); + + test('Indices - renders the indices tab', async ({ pageObjects, page }) => { + // Navigate to the indices tab + await pageObjects.indexManagement.changeTabs('indicesTab'); + + // Verify url + expect(page.url()).toContain('/indices'); + + // Verify content - wait for table to be visible + await expect(page.testSubj.locator('indexTable')).toBeVisible(); + }); + + test('Indices - can create an index', async ({ pageObjects }) => { + await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.indexManagement.setCreateIndexName(testIndexName); + await pageObjects.indexManagement.setCreateIndexMode('Lookup'); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); + await expect(pageObjects.indexManagement.indexLink(testIndexName)).toBeVisible({ + timeout: 30000, + }); + }); + + test('Data streams - renders the data streams tab', async ({ pageObjects, page }) => { + // Navigate to the data streams tab + await pageObjects.indexManagement.changeTabs('data_streamsTab'); + + // Verify url + expect(page.url()).toContain('/data_streams'); + + // Verify content - wait for table to be visible + await expect(page.testSubj.locator('dataStreamTable')).toBeVisible(); + }); + + test('Index templates - renders the index templates tab', async ({ pageObjects, page }) => { + // Navigate to the index templates tab + await pageObjects.indexManagement.changeTabs('templatesTab'); + + // Verify url + expect(page.url()).toContain('/templates'); + + // Verify content + await expect(page.testSubj.locator('templateList')).toBeVisible(); + }); + + test('Component templates - renders the component templates tab', async ({ + pageObjects, + page, + }) => { + // Navigate to the component templates tab + await pageObjects.indexManagement.changeTabs('component_templatesTab'); + + // Verify url + expect(page.url()).toContain('/component_templates'); + + // Verify content. Component templates may have been created by other apps, e.g. Ingest Manager, + // so we don't make any assertion about the presence or absence of component templates. + await expect(page.testSubj.locator('componentTemplateList')).toBeVisible(); + }); + + test('Enrich policies - renders the enrich policies tab', async ({ pageObjects, page }) => { + // Navigate to the enrich policies tab + await pageObjects.indexManagement.changeTabs('enrich_policiesTab'); + + // Verify url + expect(page.url()).toContain('/enrich_policies'); + + // Verify content + await expect(page.testSubj.locator('sectionEmpty')).toBeVisible(); + }); +}); diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_details_page.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_details_page.spec.ts new file mode 100644 index 0000000000000..1ddd2dc68aadb --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_details_page.spec.ts @@ -0,0 +1,28 @@ +/* + * 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 { test } from '../fixtures'; + +test.describe('Index details page', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ pageObjects, browserAuth }) => { + await browserAuth.loginAsIndexManagementUser(); + await pageObjects.indexManagement.goto(); + }); + + test('Navigates to the index details page from the home page', async ({ pageObjects, log }) => { + await log.debug('Navigating to the index details page'); + + // Display hidden indices to have some rows in the indices table + await pageObjects.indexManagement.toggleHiddenIndices(); + + // Click the first index in the table and wait for the index details page + await pageObjects.indexManagement.openIndexDetailsPage(0); + + // Verify index details page is loaded + await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); + }); +}); diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard.spec.ts new file mode 100644 index 0000000000000..8fed11f25349a --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard.spec.ts @@ -0,0 +1,76 @@ +/* + * 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/scout'; +import { test } from '../fixtures'; + +test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { + test.afterEach(async ({ esClient, log }) => { + try { + await esClient.indices.deleteIndexTemplate({ name: 'test-index-template' }); + } catch (e) { + log.debug( + `Template cleanup failed for test-index-template: ${ + e instanceof Error ? e.message : String(e) + }` + ); + } + }); + + test('can walk through all wizard steps and create a template', async ({ + page, + pageObjects, + browserAuth, + }) => { + await browserAuth.loginAsIndexManagementUser(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); + await page.testSubj.locator('createTemplateButton').click(); + + await test.step('verify page title', async () => { + await expect(page.testSubj.locator('pageTitle')).toBeVisible(); + await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); + }); + + await test.step('1: Logistics', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); + await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); + }); + + await test.step('2: Component templates', async () => { + await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('3: Index settings', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); + await expect(page.testSubj.locator('indexModeCallout')).toHaveText( + 'The index.mode setting has been set to Standard within the Logistics step. Any changes to index.mode set on this page will be overwritten by the Logistics selection.' + ); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('4: Mappings', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('5: Aliases', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('6: Review and create template', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText( + "Review details for 'test-index-template'" + ); + await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); + await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); + await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); + await pageObjects.indexManagement.clickNextButton(); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_mappings.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_mappings.spec.ts new file mode 100644 index 0000000000000..150bda219b6a2 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_mappings.spec.ts @@ -0,0 +1,121 @@ +/* + * 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, EuiFieldTextWrapper } from '@kbn/scout'; +import { test } from '../fixtures'; + +test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ browserAuth, pageObjects, page }) => { + await browserAuth.loginAsIndexManagementUser(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); + + // Click Create Template button + await page.testSubj.locator('createTemplateButton').click(); + + // Fill out required fields using EUI wrapper + const nameField = new EuiFieldTextWrapper(page, { dataTestSubj: 'nameField' }); + await nameField.fill('test-index-template'); + const indexPatternsField = new EuiFieldTextWrapper(page, { + dataTestSubj: 'indexPatternsField', + }); + await indexPatternsField.fill('test-index-pattern'); + + // Go to Mappings step + await page.testSubj.locator('formWizardStep-3').click(); + await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); + }); + + test.afterEach(async ({ esClient, log }) => { + try { + await esClient.indices.deleteIndexTemplate({ name: 'test-index-template' }); + } catch (e) { + log.debug( + `Template cleanup failed in mappings test: ${e instanceof Error ? e.message : String(e)}` + ); + } + }); + + test("clearing up the Numeric subtype dropdown doesn't break the page", async ({ + page, + pageObjects, + }) => { + // The Create Field form is already open by default when mappings are empty + // Select Numeric type + await pageObjects.indexManagement.setComboBox('fieldType', 'Numeric'); + + // Clear up subtype dropdown + await page.testSubj.locator('fieldSubType').click(); + await page.keyboard.press('Backspace'); + + // Verify that elements are still visible + await expect(page.testSubj.locator('addFieldButton')).toBeVisible(); + await expect(page.testSubj.locator('fieldType')).toBeVisible(); + await expect(page.testSubj.locator('fieldSubType')).toBeVisible(); + await expect(page.testSubj.locator('nextButton')).toBeVisible(); + }); + + test("clearing up the Range subtype dropdown doesn't break the page", async ({ + page, + pageObjects, + }) => { + // The Create Field form is already open by default when mappings are empty + // Select Range type + await pageObjects.indexManagement.setComboBox('fieldType', 'Range'); + + // Clear up subtype dropdown + await page.testSubj.locator('fieldSubType').click(); + await page.keyboard.press('Backspace'); + + // Verify that elements are still visible + await expect(page.testSubj.locator('addFieldButton')).toBeVisible(); + await expect(page.testSubj.locator('fieldType')).toBeVisible(); + await expect(page.testSubj.locator('fieldSubType')).toBeVisible(); + await expect(page.testSubj.locator('nextButton')).toBeVisible(); + }); + + test("advanced options tab doesn't add default values to request by default", async ({ + page, + pageObjects, + log, + }) => { + await pageObjects.indexManagement.changeMappingsEditorTab('advancedOptions'); + await page.testSubj.locator('previewIndexTemplate').click(); + const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); + + log.debug(`Template preview text: ${templatePreview}`); + + // All advanced options should not be part of the request + expect(templatePreview).not.toContain('"dynamic"'); + expect(templatePreview).not.toContain('"subobjects"'); + expect(templatePreview).not.toContain('"dynamic_date_formats"'); + expect(templatePreview).not.toContain('"date_detection"'); + expect(templatePreview).not.toContain('"numeric_detection"'); + }); + + test('advanced options tab adds the set values to the request', async ({ + page, + pageObjects, + log, + }) => { + await pageObjects.indexManagement.changeMappingsEditorTab('advancedOptions'); + + // Toggle the subobjects field to false + await page.testSubj.locator('subobjectsToggle').click(); + + await page.testSubj.locator('previewIndexTemplate').click(); + const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); + + log.debug(`Template preview text: ${templatePreview}`); + + // Only the subobjects option should be part of the request + expect(templatePreview).toContain('"subobjects": false'); + expect(templatePreview).not.toContain('"dynamic"'); + expect(templatePreview).not.toContain('"dynamic_date_formats"'); + expect(templatePreview).not.toContain('"date_detection"'); + expect(templatePreview).not.toContain('"numeric_detection"'); + }); +}); diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_preview.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_preview.spec.ts new file mode 100644 index 0000000000000..d484a3056cb44 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_preview.spec.ts @@ -0,0 +1,77 @@ +/* + * 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, EuiFieldTextWrapper } from '@kbn/scout'; +import { test } from '../fixtures'; + +test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () => { + test.afterEach(async ({ esClient, log }) => { + try { + await esClient.indices.deleteIndexTemplate({ name: 'a-star' }); + } catch (e) { + log.debug( + `Template cleanup failed for a-star: ${e instanceof Error ? e.message : String(e)}` + ); + } + }); + + test('can create and preview an index template', async ({ page, pageObjects, browserAuth }) => { + await browserAuth.loginAsIndexManagementUser(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); + + await test.step('open wizard and fill logistics', async () => { + await page.testSubj.locator('createTemplateButton').click(); + await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); + await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); + + const nameField = new EuiFieldTextWrapper(page, { dataTestSubj: 'nameField' }); + await nameField.fill('a-star'); + const indexPatternsField = new EuiFieldTextWrapper(page, { + dataTestSubj: 'indexPatternsField', + }); + await indexPatternsField.fill('a*'); + const priorityField = new EuiFieldTextWrapper(page, { dataTestSubj: 'priorityField' }); + await priorityField.fill('1000'); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('skip component templates', async () => { + await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('skip index settings', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('skip mappings', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('skip aliases', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('review and create template', async () => { + await expect(page.testSubj.locator('stepTitle')).toHaveText("Review details for 'a-star'"); + await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); + await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); + await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); + await pageObjects.indexManagement.clickNextButton(); + }); + + await test.step('preview template and verify no errors', async () => { + await page.testSubj.locator('previewTabBtn').click(); + const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); + expect(templatePreview).not.toContain('error'); + await page.testSubj.locator('closeDetailsButton').click(); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/index_management/tsconfig.json b/x-pack/platform/plugins/shared/index_management/tsconfig.json index 9c508f02c85d4..c540c453b58b7 100644 --- a/x-pack/platform/plugins/shared/index_management/tsconfig.json +++ b/x-pack/platform/plugins/shared/index_management/tsconfig.json @@ -65,7 +65,8 @@ "@kbn/inference-common", "@kbn/upgrade-assistant-pkg-common", "@kbn/failure-store-modal", - "@kbn/react-query" + "@kbn/react-query", + "@kbn/scout" ], "exclude": ["target/**/*"] } diff --git a/x-pack/platform/test/functional/apps/index_management/home_page.ts b/x-pack/platform/test/functional/apps/index_management/home_page.ts deleted file mode 100644 index 3cdcadef65b40..0000000000000 --- a/x-pack/platform/test/functional/apps/index_management/home_page.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 ({ getPageObjects, getService }: FtrProviderContext) => { - const testSubjects = getService('testSubjects'); - const pageObjects = getPageObjects(['common', 'indexManagement', 'header']); - const log = getService('log'); - const browser = getService('browser'); - const retry = getService('retry'); - const security = getService('security'); - const es = getService('es'); - - describe('Home page', function () { - before(async () => { - await security.testUser.setRoles(['index_management_user']); - await pageObjects.common.navigateToApp('indexManagement'); - }); - - it('Loads the app and renders the indices tab by default', async () => { - await log.debug('Checking for section heading to say Index Management.'); - - const headingText = await pageObjects.indexManagement.sectionHeadingText(); - expect(headingText).to.be('Index Management'); - - // Verify url - const url = await browser.getCurrentUrl(); - expect(url).to.contain(`/indices`); - - // Verify content - const indicesList = await testSubjects.exists('indicesList'); - expect(indicesList).to.be(true); - - const reloadIndicesButton = await pageObjects.indexManagement.reloadIndicesButton(); - expect(await reloadIndicesButton.isDisplayed()).to.be(true); - }); - - describe('Indices', function () { - const testIndexName = `index-test-${Math.random()}`; - - it('renders the indices tab', async () => { - // Navigate to the data streams tab - await pageObjects.indexManagement.changeTabs('indicesTab'); - - await pageObjects.header.waitUntilLoadingHasFinished(); - - // Verify url - const url = await browser.getCurrentUrl(); - expect(url).to.contain(`/indices`); - - // Verify content - await retry.waitFor('Wait until indices table is visible.', async () => { - return await testSubjects.isDisplayed('indexTable'); - }); - }); - - it('can create an index', async () => { - await pageObjects.indexManagement.clickCreateIndexButton(); - await pageObjects.indexManagement.setCreateIndexName(testIndexName); - await pageObjects.indexManagement.setCreateIndexMode('Lookup'); - await pageObjects.indexManagement.clickCreateIndexSaveButton(); - await pageObjects.indexManagement.expectIndexToExist(testIndexName); - }); - - after(async () => { - try { - await es.indices.delete({ index: testIndexName }); - } catch (e) { - log.debug(`Index cleanup failed for ${testIndexName}: ${e.message}`); - } - }); - }); - - describe('Data streams', () => { - it('renders the data streams tab', async () => { - // Navigate to the data streams tab - await pageObjects.indexManagement.changeTabs('data_streamsTab'); - - await pageObjects.header.waitUntilLoadingHasFinished(); - - // Verify url - const url = await browser.getCurrentUrl(); - expect(url).to.contain(`/data_streams`); - - // Verify content - await retry.waitFor('Wait until dataStream Table is visible.', async () => { - return await testSubjects.isDisplayed('dataStreamTable'); - }); - }); - }); - - describe('Index templates', () => { - it('renders the index templates tab', async () => { - // Navigate to the index templates tab - await pageObjects.indexManagement.changeTabs('templatesTab'); - - await pageObjects.header.waitUntilLoadingHasFinished(); - - // Verify url - const url = await browser.getCurrentUrl(); - expect(url).to.contain(`/templates`); - - // Verify content - const templateList = await testSubjects.exists('templateList'); - expect(templateList).to.be(true); - }); - }); - - describe('Component templates', () => { - it('renders the component templates tab', async () => { - // Navigate to the component templates tab - await pageObjects.indexManagement.changeTabs('component_templatesTab'); - - await pageObjects.header.waitUntilLoadingHasFinished(); - - // Verify url - const url = await browser.getCurrentUrl(); - expect(url).to.contain(`/component_templates`); - - // Verify content. Component templates may have been created by other apps, e.g. Ingest Manager, - // so we don't make any assertion about the presence or absence of component templates. - const componentTemplateList = await testSubjects.exists('componentTemplateList'); - expect(componentTemplateList).to.be(true); - }); - }); - - describe('Enrich policies', () => { - it('renders the enrich policies tab', async () => { - // Navigate to the enrich policies tab - await pageObjects.indexManagement.changeTabs('enrich_policiesTab'); - - await pageObjects.header.waitUntilLoadingHasFinished(); - - // Verify url - const url = await browser.getCurrentUrl(); - expect(url).to.contain(`/enrich_policies`); - - // Verify content - const enrichPoliciesList = await testSubjects.exists('sectionEmpty'); - expect(enrichPoliciesList).to.be(true); - }); - }); - }); -}; diff --git a/x-pack/platform/test/functional/apps/index_management/index.ts b/x-pack/platform/test/functional/apps/index_management/index.ts index 20303af196119..3da18702e6e16 100644 --- a/x-pack/platform/test/functional/apps/index_management/index.ts +++ b/x-pack/platform/test/functional/apps/index_management/index.ts @@ -10,9 +10,6 @@ import type { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Index Management app', function () { loadTestFile(require.resolve('./feature_controls')); - loadTestFile(require.resolve('./home_page')); - loadTestFile(require.resolve('./index_template_wizard')); - loadTestFile(require.resolve('./index_details_page')); loadTestFile(require.resolve('./enrich_policies_tab')); loadTestFile(require.resolve('./create_enrich_policy')); loadTestFile(require.resolve('./data_streams_tab')); diff --git a/x-pack/platform/test/functional/apps/index_management/index_details_page.ts b/x-pack/platform/test/functional/apps/index_management/index_details_page.ts deleted file mode 100644 index fc35a1212c610..0000000000000 --- a/x-pack/platform/test/functional/apps/index_management/index_details_page.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 ({ getPageObjects, getService }: FtrProviderContext) => { - const pageObjects = getPageObjects(['common', 'indexManagement', 'header']); - const log = getService('log'); - const security = getService('security'); - - describe('Index details page', function () { - before(async () => { - await security.testUser.setRoles(['index_management_user']); - await pageObjects.common.navigateToApp('indexManagement'); - }); - - it('Navigates to the index details page from the home page', async () => { - await log.debug('Navigating to the index details page'); - - // display hidden indices to have some rows in the indices table - await pageObjects.indexManagement.toggleHiddenIndices(); - // click the first index in the table and wait for the index details page - await pageObjects.indexManagement.indexDetailsPage.openIndexDetailsPage(0); - await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); - }); - }); -}; diff --git a/x-pack/platform/test/functional/apps/index_management/index_template_wizard.ts b/x-pack/platform/test/functional/apps/index_management/index_template_wizard.ts deleted file mode 100644 index 3d5d0b1e8e26f..0000000000000 --- a/x-pack/platform/test/functional/apps/index_management/index_template_wizard.ts +++ /dev/null @@ -1,307 +0,0 @@ -/* - * 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 ({ getPageObjects, getService }: FtrProviderContext) => { - const testSubjects = getService('testSubjects'); - const pageObjects = getPageObjects(['common', 'indexManagement', 'header']); - const security = getService('security'); - const comboBox = getService('comboBox'); - const find = getService('find'); - const browser = getService('browser'); - const log = getService('log'); - const es = getService('es'); - - describe('Index template wizard', function () { - before(async () => { - await security.testUser.setRoles(['index_management_user']); - }); - - describe('Create', () => { - before(async () => { - await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - // Click Create Template button - await testSubjects.click('createTemplateButton'); - }); - - after(async () => { - try { - await es.indices.deleteIndexTemplate({ name: 'test-index-template' }); - await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - } catch (e) { - log.debug(`Template cleanup failed for test-index-template: ${e.message}`); - } - }); - - it('should set the correct page title', async () => { - const pageTitle = await testSubjects.exists('pageTitle'); - expect(pageTitle).to.be(true); - - const pageTitleText = await testSubjects.getVisibleText('pageTitle'); - expect(pageTitleText).to.be('Create template'); - }); - - it('renders logistics (step 1)', async () => { - // Verify step title - const stepTitle = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle).to.be('Logistics'); - - // Fill out required fields - await testSubjects.setValue('nameField', 'test-index-template'); - await testSubjects.setValue('indexPatternsField', 'test-index-pattern'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - it('renders component templates (step 2)', async () => { - // Verify empty prompt - const emptyPrompt = await testSubjects.exists('emptyPrompt'); - expect(emptyPrompt).to.be(true); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - it('renders index settings (step 3)', async () => { - // Verify step title - const stepTitle = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle).to.be('Index settings (optional)'); - - // Verify that index mode callout is displayed - const indexModeCalloutText = await testSubjects.getVisibleText('indexModeCallout'); - expect(indexModeCalloutText).to.be( - 'The index.mode setting has been set to Standard within the Logistics step. Any changes to index.mode set on this page will be overwritten by the Logistics selection.' - ); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - it('renders mappings (step 4)', async () => { - // Verify step title - const stepTitle = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle).to.be('Mappings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - it('renders aliases (step 5)', async () => { - // Verify step title - const stepTitle = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle).to.be('Aliases (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - it('renders review template (step 6)', async () => { - // Verify step title - const stepTitle = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle).to.be("Review details for 'test-index-template'"); - - // Verify that summary exists - const summaryTabContent = await testSubjects.exists('summaryTabContent'); - expect(summaryTabContent).to.be(true); - - // Verify that index mode is set to "Standard" - expect(await testSubjects.exists('indexModeTitle')).to.be(true); - expect(await testSubjects.getVisibleText('indexModeValue')).to.be('Standard'); - - // Click Create template - await pageObjects.indexManagement.clickNextButton(); - }); - }); - - // https://github.com/elastic/kibana/pull/195174 - describe('Preview template test', () => { - before(async () => { - await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - }); - - after(async () => { - try { - await es.indices.deleteIndexTemplate({ name: 'a-star' }); - } catch (e) { - log.debug(`Template cleanup failed for a-star: ${e.message}`); - } - }); - - it('can preview index template that matches a_fake_index_pattern_that_wont_match_any_indices', async () => { - // Click Create Template button - await testSubjects.click('createTemplateButton'); - const pageTitleText = await testSubjects.getVisibleText('pageTitle'); - expect(pageTitleText).to.be('Create template'); - - const stepTitle1 = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle1).to.be('Logistics'); - - // Fill out required fields - await testSubjects.setValue('nameField', 'a-star'); - await testSubjects.setValue('indexPatternsField', 'a*'); - await testSubjects.setValue('priorityField', '1000'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify empty prompt - const emptyPrompt = await testSubjects.exists('emptyPrompt'); - expect(emptyPrompt).to.be(true); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - const stepTitle2 = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle2).to.be('Index settings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - const stepTitle3 = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle3).to.be('Mappings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - const stepTitle4 = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle4).to.be('Aliases (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - const stepTitle = await testSubjects.getVisibleText('stepTitle'); - expect(stepTitle).to.be("Review details for 'a-star'"); - - // Verify that summary exists - const summaryTabContent = await testSubjects.exists('summaryTabContent'); - expect(summaryTabContent).to.be(true); - - // Verify that index mode is set to "Standard" - expect(await testSubjects.exists('indexModeTitle')).to.be(true); - expect(await testSubjects.getVisibleText('indexModeValue')).to.be('Standard'); - - // Click Create template - await pageObjects.indexManagement.clickNextButton(); - - // Click preview tab, we know its the last one - const tabs = await testSubjects.findAll('tab'); - await tabs[tabs.length - 1].click(); - const templatePreview = await testSubjects.getVisibleText('simulateTemplatePreview'); - expect(templatePreview).to.not.contain('error'); - - await testSubjects.click('closeDetailsButton'); - }); - }); - describe('Mappings step', () => { - beforeEach(async () => { - await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - - // Click Create Template button - await testSubjects.click('createTemplateButton'); - - // Fill out required fields - await testSubjects.setValue('nameField', 'test-index-template'); - await testSubjects.setValue('indexPatternsField', 'test-index-pattern'); - - // Go to Mappings step - await testSubjects.click('formWizardStep-3'); - expect(await testSubjects.getVisibleText('stepTitle')).to.be('Mappings (optional)'); - }); - - afterEach(async () => { - try { - await es.indices.deleteIndexTemplate({ name: 'test-index-template' }); - } catch (e) { - log.debug(`Template cleanup failed in mappings test: ${e.message}`); - } - }); - - // Test for catching the bug reported in https://github.com/elastic/kibana/issues/156202 - it("clearing up the Numeric subtype dropdown doesn't break the page", async () => { - // Add a mapping field - await testSubjects.click('addFieldButton'); - - // Select Numeric type - await testSubjects.click('fieldType'); - await comboBox.set('fieldType', 'Numeric'); - - // Clear up subtype dropdown - await testSubjects.click('fieldSubType'); - const input = await find.activeElement(); - await input.pressKeys(browser.keys.BACK_SPACE); - - // Verify that elements are still visible - expect(await testSubjects.exists('addFieldButton')).to.be(true); - expect(await testSubjects.exists('fieldType')).to.be(true); - expect(await testSubjects.exists('fieldSubType')).to.be(true); - expect(await testSubjects.exists('nextButton')).to.be(true); - }); - - // Test for catching the bug reported in https://github.com/elastic/kibana/issues/156202 - it("clearing up the Range subtype dropdown doesn't break the page", async () => { - // Add a mapping field - await testSubjects.click('addFieldButton'); - - // Select Range type - await testSubjects.click('fieldType'); - await comboBox.set('fieldType', 'Range'); - - // Clear up subtype dropdown - await testSubjects.click('fieldSubType'); - const input = await find.activeElement(); - await input.pressKeys(browser.keys.BACK_SPACE); - - // Verify that elements are still visible - expect(await testSubjects.exists('addFieldButton')).to.be(true); - expect(await testSubjects.exists('fieldType')).to.be(true); - expect(await testSubjects.exists('fieldSubType')).to.be(true); - expect(await testSubjects.exists('nextButton')).to.be(true); - }); - - it("advanced options tab doesn't add default values to request by default", async () => { - await pageObjects.indexManagement.changeMappingsEditorTab('advancedOptions'); - await testSubjects.click('previewIndexTemplate'); - const templatePreview = await testSubjects.getVisibleText('simulateTemplatePreview'); - - await log.debug(`Template preview text: ${templatePreview}`); - - // All advanced options should not be part of the request - expect(templatePreview).to.not.contain('"dynamic"'); - expect(templatePreview).to.not.contain('"subobjects"'); - expect(templatePreview).to.not.contain('"dynamic_date_formats"'); - expect(templatePreview).to.not.contain('"date_detection"'); - expect(templatePreview).to.not.contain('"numeric_detection"'); - }); - - it('advanced options tab adds the set values to the request', async () => { - await pageObjects.indexManagement.changeMappingsEditorTab('advancedOptions'); - - // Toggle the subobjects field to false - await testSubjects.click('subobjectsToggle'); - - await testSubjects.click('previewIndexTemplate'); - const templatePreview = await testSubjects.getVisibleText('simulateTemplatePreview'); - - await log.debug(`Template preview text: ${templatePreview}`); - - // Only the subobjects option should be part of the request - expect(templatePreview).to.contain('"subobjects": false'); - expect(templatePreview).to.not.contain('"dynamic"'); - expect(templatePreview).to.not.contain('"dynamic_date_formats"'); - expect(templatePreview).to.not.contain('"date_detection"'); - expect(templatePreview).to.not.contain('"numeric_detection"'); - }); - }); - }); -}; diff --git a/x-pack/platform/test/functional/apps/index_management/index_templates_tab/index_template_tab.ts b/x-pack/platform/test/functional/apps/index_management/index_templates_tab/index_template_tab.ts index 3fa29ca06911c..3bd4a7ec2da3f 100644 --- a/x-pack/platform/test/functional/apps/index_management/index_templates_tab/index_template_tab.ts +++ b/x-pack/platform/test/functional/apps/index_management/index_templates_tab/index_template_tab.ts @@ -140,8 +140,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Navigate to Mappings await testSubjects.click('formWizardStep-3'); await pageObjects.header.waitUntilLoadingHasFinished(); - const mappingTabs = await testSubjects.findAll('formTab'); - await mappingTabs[3].click(); + await testSubjects.click('advancedOptionsTab'); // Modify timestamp format await testSubjects.click('comboBoxClearButton'); @@ -159,10 +158,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.indexManagement.clickNextButton(); await pageObjects.header.waitUntilLoadingHasFinished(); - const flyoutTabs = await testSubjects.findAll('tab'); - // Verify Index Settings - await flyoutTabs[1].click(); + await testSubjects.click('settingsTabBtn'); await pageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.exists('settingsTabContent')).to.be(true); const settingsTabContent = await testSubjects.getVisibleText('settingsTabContent'); @@ -180,7 +177,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); // Verify Mappings - await flyoutTabs[2].click(); + await testSubjects.click('mappingsTabBtn'); await pageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.exists('mappingsTabContent')).to.be(true); const mappingsTabContent = await testSubjects.getVisibleText('mappingsTabContent'); @@ -197,8 +194,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Navigate to Mappings await testSubjects.click('formWizardStep-3'); await pageObjects.header.waitUntilLoadingHasFinished(); - const mappingTabs = await testSubjects.findAll('formTab'); - await mappingTabs[3].click(); + await (await testSubjects.find('advancedOptionsTab')).click(); // Modify source await testSubjects.click('sourceValueField'); diff --git a/x-pack/platform/test/serverless/functional/test_suites/management/index_management/index_templates.ts b/x-pack/platform/test/serverless/functional/test_suites/management/index_management/index_templates.ts index 6dbc821d50f10..bba604253755e 100644 --- a/x-pack/platform/test/serverless/functional/test_suites/management/index_management/index_templates.ts +++ b/x-pack/platform/test/serverless/functional/test_suites/management/index_management/index_templates.ts @@ -176,8 +176,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Navigate to Mappings await testSubjects.click('formWizardStep-3'); await pageObjects.header.waitUntilLoadingHasFinished(); - const mappingTabs = await testSubjects.findAll('formTab'); - await mappingTabs[3].click(); + await testSubjects.click('advancedOptionsTab'); // Modify timestamp format await testSubjects.click('comboBoxClearButton'); @@ -195,10 +194,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.indexManagement.clickNextButton(); await pageObjects.header.waitUntilLoadingHasFinished(); - const flyoutTabs = await testSubjects.findAll('tab'); - // Verify Index Settings - await flyoutTabs[1].click(); + await testSubjects.click('settingsTabBtn'); await pageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.exists('settingsTabContent')).to.be(true); const settingsTabContent = await testSubjects.getVisibleText('settingsTabContent'); @@ -216,7 +213,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); // Verify Mappings - await flyoutTabs[2].click(); + await testSubjects.click('mappingsTabBtn'); await pageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.exists('mappingsTabContent')).to.be(true); const mappingsTabContent = await testSubjects.getVisibleText('mappingsTabContent');