From c354e7e83459524a1aad86dcc9825ba38f667f9a Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 13 Nov 2025 00:27:08 -0600 Subject: [PATCH 01/41] home_page test port --- .buildkite/scout_ci_config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.buildkite/scout_ci_config.yml b/.buildkite/scout_ci_config.yml index f63440018e772..09c55901434a5 100644 --- a/.buildkite/scout_ci_config.yml +++ b/.buildkite/scout_ci_config.yml @@ -3,6 +3,7 @@ plugins: enabled: - apm - discover_enhanced + - index_management - maps - observability - observability_onboarding From 36d966a593d600f6b37537ad71eb36b1b8aa6971 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 13 Nov 2025 00:28:22 -0600 Subject: [PATCH 02/41] home_page test port --- .../test/scout/ui/fixtures/index.ts | 32 +++++ .../scout/ui/fixtures/page_objects/index.ts | 24 ++++ .../page_objects/index_management_page.ts | 81 +++++++++++++ .../test/scout/ui/playwright.config.ts | 12 ++ .../test/scout/ui/tests/home_page.spec.ts | 111 ++++++++++++++++++ 5 files changed, 260 insertions(+) create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/index.ts create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index.ts create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index_management_page.ts create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/playwright.config.ts create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/home_page.spec.ts 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..7f79274779652 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/index.ts @@ -0,0 +1,32 @@ +/* + * 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 } from '@kbn/scout'; + +import type { IndexManagementPageObjects } from './page_objects'; +import { extendPageObjects } from './page_objects'; + +export interface ConsoleTestFixtures extends ScoutTestFixtures { + pageObjects: IndexManagementPageObjects; +} + +export const test = base.extend({ + pageObjects: async ( + { + pageObjects, + page, + }: { + pageObjects: IndexManagementPageObjects; + page: ScoutPage; + }, + use: (pageObjects: IndexManagementPageObjects) => Promise + ) => { + const extendedPageObjects = extendPageObjects(pageObjects, page); + await use(extendedPageObjects); + }, +}); 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..671eb1e79192a --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index_management_page.ts @@ -0,0 +1,81 @@ +/* + * 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 { ScoutPage } from '@kbn/scout'; + +export class IndexManagement { + constructor(public readonly page: ScoutPage) {} + + 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) { + const nameField = this.page.testSubj.locator('createIndexNameFieldText'); + await nameField.waitFor({ state: 'visible' }); + await nameField.fill(value); + } + + async setCreateIndexMode(value: string) { + const modeField = this.page.testSubj.locator('indexModeField'); + await modeField.waitFor({ state: 'visible' }); + // await this.page.pause(); + await modeField.click(); + await this.page.testSubj.locator(`indexMode${value}Option`).click(); + } + + async clickCreateIndexSaveButton() { + const saveButton = this.page.testSubj.locator('createIndexSaveButton'); + await saveButton.waitFor({ state: 'visible' }); + await saveButton.click(); + // Wait for modal to close + await saveButton.waitFor({ state: 'hidden', timeout: 30000 }); + } + + async expectIndexToExist(indexName: string) { + // Wait for the table to be visible + const table = this.page.locator('table'); + await table.waitFor({ state: 'visible' }); + + // Get all index name links + const indexLinks = this.page.testSubj.locator('indexTableIndexNameLink'); + const count = await indexLinks.count(); + + // Check if any of the links contain the index name + let found = false; + for (let i = 0; i < count; i++) { + const text = await indexLinks.nth(i).textContent(); + if (text === indexName) { + found = true; + break; + } + } + + if (!found) { + throw new Error(`Expected index "${indexName}" to exist in the table, but it was not found`); + } + } +} 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..fe2c90cb23070 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/home_page.spec.ts @@ -0,0 +1,111 @@ +/* + * 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('Home page', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ browserAuth, pageObjects }) => { + // TODO: Create loginAsIndexManagementUser role + await browserAuth.loginAsAdmin(); + await pageObjects.indexManagement.goto(); + }); + + 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, esClient, log }) => { + const testIndexName = `index-test-${Math.random()}`; + + await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.indexManagement.setCreateIndexName(testIndexName); + await pageObjects.indexManagement.setCreateIndexMode('Lookup'); + await pageObjects.indexManagement.clickCreateIndexSaveButton(); + await pageObjects.indexManagement.expectIndexToExist(testIndexName); + + // Cleanup + try { + await esClient.indices.delete({ index: testIndexName }); + } catch (e: any) { + log.debug(`Index cleanup failed for ${testIndexName}: ${e.message}`); + } + }); + + 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(); + }); +}); From f3b31407dc03e78909b4553c0c45e996b54db936 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 13 Nov 2025 06:39:22 +0000 Subject: [PATCH 03/41] Changes from node scripts/lint_ts_projects --fix --- x-pack/platform/plugins/shared/index_management/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/index_management/tsconfig.json b/x-pack/platform/plugins/shared/index_management/tsconfig.json index 45a1243ac270f..a8e5357b3ed39 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/**/*"] } From e7f7760a27feae49bc9cbc16281b94674fb05368 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 13 Nov 2025 09:26:17 -0600 Subject: [PATCH 04/41] fix lint issues --- .../page_objects/index_management_page.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) 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 index 671eb1e79192a..4f22669d6f6cf 100644 --- 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 @@ -67,6 +67,8 @@ export class IndexManagement { // Check if any of the links contain the index name let found = false; for (let i = 0; i < count; i++) { + // todo + // eslint-disable-next-line playwright/no-nth-methods const text = await indexLinks.nth(i).textContent(); if (text === indexName) { found = true; @@ -78,4 +80,59 @@ export class IndexManagement { throw new Error(`Expected index "${indexName}" to exist in the table, but it was not found`); } } + + async toggleHiddenIndices() { + await this.page.testSubj.locator('checkboxToggles-includeHiddenIndices').click(); + } + + async openIndexDetailsPage(indexOfRow: number) { + const indexLinks = this.page.testSubj.locator('indexTableIndexNameLink'); + // todo + // eslint-disable-next-line playwright/no-nth-methods + await indexLinks.nth(indexOfRow).click(); + + // Wait for index details page to load + await this.page.testSubj.locator('indexDetailsHeader').waitFor({ state: 'visible' }); + } + + 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(); + } + + 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 + const option = this.page.locator(`[role="option"]`).filter({ hasText: value }); + await option.waitFor({ state: 'visible' }); + await option.click(); + } + + async changeMappingsEditorTab(tab: 'fields' | 'advancedOptions' | 'templates') { + const tabMap = { + fields: 'formTab', + advancedOptions: 'advancedOptionsTab', + templates: 'templatesTab', + }; + await this.page.testSubj.locator(tabMap[tab]).click(); + } } From b32f61fb5b7829710b8735ca3c7fb12437b239e6 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 13 Nov 2025 09:27:08 -0600 Subject: [PATCH 05/41] fix lint issues --- .../scout/ui/tests/index_details_page.spec.ts | 37 ++ .../ui/tests/index_template_wizard.spec.ts | 344 ++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_details_page.spec.ts create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard.spec.ts 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..fce4d056edbd7 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_details_page.spec.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/scout'; +import { test } from '../fixtures'; + +test.describe('Index details page', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ browserAuth, pageObjects }) => { + // TODO: Create loginAsIndexManagementUser role + await browserAuth.loginAsAdmin(); + await pageObjects.indexManagement.goto(); + }); + + test('Navigates to the index details page from the home page', async ({ + pageObjects, + log, + page, + }) => { + 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 expect(page.testSubj.locator('indexDetailsTab-overview')).toBeVisible(); + await expect(page.testSubj.locator('indexDetailsContent')).toBeVisible(); + await expect(page.testSubj.locator('indexDetailsBackToIndicesButton')).toBeVisible(); + }); +}); + 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..e82ad55b33805 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard.spec.ts @@ -0,0 +1,344 @@ +/* + * 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.beforeEach(async ({ browserAuth, pageObjects, page }) => { + // TODO: Create loginAsIndexManagementUser role + await browserAuth.loginAsAdmin(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); + // Click Create Template button + await page.testSubj.locator('createTemplateButton').click(); + }); + + 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('should set the correct page title', async ({ page }) => { + await expect(page.testSubj.locator('pageTitle')).toBeVisible(); + await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); + }); + + test('renders logistics (step 1)', async ({ page, pageObjects }) => { + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); + + // Fill out required fields + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + }); + + test('renders component templates (step 2)', async ({ page, pageObjects }) => { + // Fill logistics step + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); + await pageObjects.indexManagement.clickNextButton(); + + // Verify empty prompt + await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + }); + + test('renders index settings (step 3)', async ({ page, pageObjects }) => { + // Fill logistics step + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); + await pageObjects.indexManagement.clickNextButton(); + + // Skip component templates step + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); + + // Verify that index mode callout is displayed + 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.' + ); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + }); + + test('renders mappings (step 4)', async ({ page, pageObjects }) => { + // Fill logistics step + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); + await pageObjects.indexManagement.clickNextButton(); + + // Skip component templates and settings steps + await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + }); + + test('renders aliases (step 5)', async ({ page, pageObjects }) => { + // Fill logistics step + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); + await pageObjects.indexManagement.clickNextButton(); + + // Skip component templates, settings, and mappings steps + await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + }); + + test('renders review template (step 6)', async ({ page, pageObjects }) => { + // Fill logistics step + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); + await pageObjects.indexManagement.clickNextButton(); + + // Skip all intermediate steps + await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText( + "Review details for 'test-index-template'" + ); + + // Verify that summary exists + await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); + + // Verify that index mode is set to "Standard" + await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); + await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); + + // Click Create template + await pageObjects.indexManagement.clickNextButton(); + }); +}); + +test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ browserAuth, pageObjects }) => { + // TODO: Create loginAsIndexManagementUser role + await browserAuth.loginAsAdmin(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); + }); + + test('can preview index template that matches a_fake_index_pattern_that_wont_match_any_indices', async ({ + page, + pageObjects, + esClient, + log, + }) => { + // Click Create Template button + await page.testSubj.locator('createTemplateButton').click(); + await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); + await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); + + // Fill out required fields + await page.testSubj.locator('nameField').locator('input').fill('a-star'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('a*'); + await page.testSubj.locator('priorityField').locator('input').fill('1000'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify empty prompt + await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText("Review details for 'a-star'"); + + // Verify that summary exists + await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); + + // Verify that index mode is set to "Standard" + await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); + await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); + + // Click Create template + await pageObjects.indexManagement.clickNextButton(); + + // Click preview tab, we know its the last one + const tabs = page.testSubj.locator('tab'); + const tabCount = await tabs.count(); + // todo + // eslint-disable-next-line playwright/no-nth-methods + await tabs.nth(tabCount - 1).click(); + + const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); + expect(templatePreview).not.toContain('error'); + + await page.testSubj.locator('closeDetailsButton').click(); + + // Cleanup + 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.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ browserAuth, pageObjects, page }) => { + // TODO: Create loginAsIndexManagementUser role + await browserAuth.loginAsAdmin(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); + + // Click Create Template button + await page.testSubj.locator('createTemplateButton').click(); + + // Fill out required fields + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').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, + }) => { + // Add a mapping field + await page.testSubj.locator('addFieldButton').click(); + + // 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, + }) => { + // Add a mapping field + await page.testSubj.locator('addFieldButton').click(); + + // 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"'); + }); +}); From 57f2ef80db55768bdc6ffaf28c0281585302efb4 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:54:06 +0000 Subject: [PATCH 06/41] Changes from node scripts/eslint_all_files --no-cache --fix --- .../test/scout/ui/tests/index_details_page.spec.ts | 1 - 1 file changed, 1 deletion(-) 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 index fce4d056edbd7..f4d0a1b7805b1 100644 --- 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 @@ -34,4 +34,3 @@ test.describe('Index details page', { tag: ['@ess'] }, () => { await expect(page.testSubj.locator('indexDetailsBackToIndicesButton')).toBeVisible(); }); }); - From e320a3af7b57e45e1acde46353bf27c1a55d1e1e Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 13 Nov 2025 17:38:27 -0600 Subject: [PATCH 07/41] make data-test-subj's unique --- .../public/application/components/no_match/no_match.tsx | 8 +++++++- .../home/index_list/create_index/create_index_button.tsx | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) 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 b1a065a98c81a..a96e4dce62f62 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 @@ -65,7 +65,13 @@ export const NoMatch = ({ if (extensionsService.emptyListContent) { return extensionsService.emptyListContent.renderContent({ - createIndexButton: , + createIndexButton: ( + + ), }); } diff --git a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx index 4200d9b0d2462..99db6adcc1001 100644 --- a/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx +++ b/x-pack/platform/plugins/shared/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx @@ -17,9 +17,10 @@ import { useAppContext } from '../../../../app_context'; export interface CreateIndexButtonProps { loadIndices: () => void; share?: SharePluginStart; + dataTestSubj?: string; } -export const CreateIndexButton = ({ loadIndices, share }: CreateIndexButtonProps) => { +export const CreateIndexButton = ({ loadIndices, share, dataTestSubj }: CreateIndexButtonProps) => { const [createIndexModalOpen, setCreateIndexModalOpen] = useState(false); const createIndexUrl = share?.url.locators.get('SEARCH_CREATE_INDEX')?.useUrl({}); @@ -40,7 +41,7 @@ export const CreateIndexButton = ({ loadIndices, share }: CreateIndexButtonProps fill iconType="plusInCircleFilled" key="createIndexButton" - data-test-subj="createIndexButton" + data-test-subj={dataTestSubj || 'createIndexButton'} data-telemetry-id="idxMgmt-indexList-createIndexButton" {...actionProp} > From 277213c6f65f617444159a52d68d4e7ccaca4330 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 07:40:17 -0600 Subject: [PATCH 08/41] functional test fixes --- .../components/mappings_editor/mappings_editor.tsx | 8 ++++---- .../public/application/components/no_match/no_match.tsx | 8 +++++++- .../ui/fixtures/page_objects/index_management_page.ts | 2 +- .../test/scout/ui/tests/index_details_page.spec.ts | 1 - .../test/scout/ui/tests/index_template_wizard.spec.ts | 5 +---- 5 files changed, 13 insertions(+), 11 deletions(-) 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/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 index 4f22669d6f6cf..75bf4e83801d3 100644 --- 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 @@ -129,7 +129,7 @@ export class IndexManagement { async changeMappingsEditorTab(tab: 'fields' | 'advancedOptions' | 'templates') { const tabMap = { - fields: 'formTab', + fields: 'fieldsTab', advancedOptions: 'advancedOptionsTab', templates: 'templatesTab', }; 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 index fce4d056edbd7..f4d0a1b7805b1 100644 --- 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 @@ -34,4 +34,3 @@ test.describe('Index details page', { tag: ['@ess'] }, () => { await expect(page.testSubj.locator('indexDetailsBackToIndicesButton')).toBeVisible(); }); }); - 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 index e82ad55b33805..857924c79aaea 100644 --- 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 @@ -263,8 +263,6 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, }) => { // Add a mapping field - await page.testSubj.locator('addFieldButton').click(); - // Select Numeric type await pageObjects.indexManagement.setComboBox('fieldType', 'Numeric'); @@ -284,8 +282,6 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, }) => { // Add a mapping field - await page.testSubj.locator('addFieldButton').click(); - // Select Range type await pageObjects.indexManagement.setComboBox('fieldType', 'Range'); @@ -305,6 +301,7 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, log, }) => { + await page.pause(); await pageObjects.indexManagement.changeMappingsEditorTab('advancedOptions'); await page.testSubj.locator('previewIndexTemplate').click(); const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); From 5c2671bd4a2027fcfc584419a01eb30895da3cd4 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 08:18:38 -0600 Subject: [PATCH 09/41] remove page.pause --- .../test/scout/ui/tests/index_template_wizard.spec.ts | 1 - 1 file changed, 1 deletion(-) 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 index 857924c79aaea..2da19dc9d794c 100644 --- 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 @@ -301,7 +301,6 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, log, }) => { - await page.pause(); await pageObjects.indexManagement.changeMappingsEditorTab('advancedOptions'); await page.testSubj.locator('previewIndexTemplate').click(); const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); From e6538db1d7132aea020c44cc42b5ed5c2c51dc06 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 11:07:22 -0600 Subject: [PATCH 10/41] fix jest tests --- .../template_create.test.tsx | 22 ++++++++----------- .../template_form.helpers.ts | 4 ++-- .../helpers/mappings_editor.helpers.tsx | 3 ++- .../mappings_editor.test.tsx | 10 ++++----- 4 files changed, 17 insertions(+), 22 deletions(-) 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/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', ]); }); From ed6f186fbcdfdea30324ade7cfd25993ce6ccba5 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 11:09:06 -0600 Subject: [PATCH 11/41] remove FTR tests that were ported --- .../apps/index_management/home_page.ts | 150 --------- .../functional/apps/index_management/index.ts | 3 - .../index_management/index_details_page.ts | 31 -- .../index_management/index_template_wizard.ts | 307 ------------------ 4 files changed, 491 deletions(-) delete mode 100644 x-pack/platform/test/functional/apps/index_management/home_page.ts delete mode 100644 x-pack/platform/test/functional/apps/index_management/index_details_page.ts delete mode 100644 x-pack/platform/test/functional/apps/index_management/index_template_wizard.ts 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"'); - }); - }); - }); -}; From 3b9ec723b40f2db1beaeebfad5e5a3a1b2e3f548 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 11:31:44 -0600 Subject: [PATCH 12/41] cleanup and refer to tabs by dataTestSubj instead of position --- .../template_details_content.tsx | 7 +++++- .../page_objects/index_management_page.ts | 22 +------------------ .../ui/tests/index_template_wizard.spec.ts | 8 ++----- 3 files changed, 9 insertions(+), 28 deletions(-) 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 138e846c2bae7..e1376cb50c2fc 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: 'summaryTab', }, { id: SETTINGS_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.settingsTabTitle', { defaultMessage: 'Settings', }), + dataTestSubj: 'settingsTab', }, { id: MAPPINGS_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.mappingsTabTitle', { defaultMessage: 'Mappings', }), + dataTestSubj: 'mappingsTab', }, { id: ALIASES_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.aliasesTabTitle', { defaultMessage: 'Aliases', }), + dataTestSubj: 'aliasesTab', }, { id: PREVIEW_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.previewTabTitle', { defaultMessage: 'Preview', }), + dataTestSubj: 'previewTab', }, ]; @@ -220,7 +225,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/page_objects/index_management_page.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index_management_page.ts index 75bf4e83801d3..b93c9043d37a7 100644 --- 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 @@ -42,7 +42,6 @@ export class IndexManagement { async setCreateIndexMode(value: string) { const modeField = this.page.testSubj.locator('indexModeField'); await modeField.waitFor({ state: 'visible' }); - // await this.page.pause(); await modeField.click(); await this.page.testSubj.locator(`indexMode${value}Option`).click(); } @@ -59,26 +58,7 @@ export class IndexManagement { // Wait for the table to be visible const table = this.page.locator('table'); await table.waitFor({ state: 'visible' }); - - // Get all index name links - const indexLinks = this.page.testSubj.locator('indexTableIndexNameLink'); - const count = await indexLinks.count(); - - // Check if any of the links contain the index name - let found = false; - for (let i = 0; i < count; i++) { - // todo - // eslint-disable-next-line playwright/no-nth-methods - const text = await indexLinks.nth(i).textContent(); - if (text === indexName) { - found = true; - break; - } - } - - if (!found) { - throw new Error(`Expected index "${indexName}" to exist in the table, but it was not found`); - } + this.page.getByText(indexName); } async toggleHiddenIndices() { 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 index 2da19dc9d794c..164cd24ae9088 100644 --- 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 @@ -207,12 +207,8 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () // Click Create template await pageObjects.indexManagement.clickNextButton(); - // Click preview tab, we know its the last one - const tabs = page.testSubj.locator('tab'); - const tabCount = await tabs.count(); - // todo - // eslint-disable-next-line playwright/no-nth-methods - await tabs.nth(tabCount - 1).click(); + // Click preview tab + page.testSubj.locator('previewTab').click(); const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); expect(templatePreview).not.toContain('error'); From 6e7b8dce2c13e9db5f8dd4106fc186d7d7e9ae73 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 12:20:47 -0600 Subject: [PATCH 13/41] use custom role --- .../index_management/test/scout/ui/tests/home_page.spec.ts | 4 +++- .../test/scout/ui/tests/index_details_page.spec.ts | 3 ++- .../test/scout/ui/tests/index_template_wizard.spec.ts | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) 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 index fe2c90cb23070..890e0556e6209 100644 --- 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 @@ -7,11 +7,13 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; +// import { CUSTOM_ROLES } from './custom_roles'; test.describe('Home page', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - // TODO: Create loginAsIndexManagementUser role + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); await browserAuth.loginAsAdmin(); + await pageObjects.indexManagement.goto(); }); 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 index f4d0a1b7805b1..e884c11586bda 100644 --- 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 @@ -7,10 +7,11 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; +// import { CUSTOM_ROLES } from './custom_roles'; test.describe('Index details page', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - // TODO: Create loginAsIndexManagementUser role + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.goto(); }); 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 index 164cd24ae9088..5a0c5bde4f459 100644 --- 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 @@ -7,10 +7,11 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; +// import { CUSTOM_ROLES } from './custom_roles'; test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects, page }) => { - // TODO: Create loginAsIndexManagementUser role + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); // Click Create Template button From fe5c01406ea8f12388ee65f224863ff68010299e Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 12:22:20 -0600 Subject: [PATCH 14/41] use custom role --- .../test/scout/ui/tests/custom_roles.ts | 30 +++++++++++++++++++ .../ui/tests/index_template_wizard.spec.ts | 4 +-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts new file mode 100644 index 0000000000000..185059698a786 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/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 = { + indexManagement: { + elasticsearch: { + // would be nice if this wasn't needed + cluster: ['monitor'], + indices: [ + { + names: ['*'], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + base: ['read'], + feature: {}, + spaces: ['*'], + }, + ], + }, +}; 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 index 5a0c5bde4f459..05096f27de34c 100644 --- 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 @@ -147,7 +147,7 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - // TODO: Create loginAsIndexManagementUser role + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); }); @@ -229,7 +229,7 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects, page }) => { - // TODO: Create loginAsIndexManagementUser role + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); From 6014af97f2f00ba7d72f85661288ce91bd5a1a32 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 12:23:57 -0600 Subject: [PATCH 15/41] remove todo --- .../scout/ui/fixtures/page_objects/index_management_page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index b93c9043d37a7..2f8e7ec8ebb9e 100644 --- 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 @@ -67,7 +67,7 @@ export class IndexManagement { async openIndexDetailsPage(indexOfRow: number) { const indexLinks = this.page.testSubj.locator('indexTableIndexNameLink'); - // todo + // 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(); From e49993027261e10c22f9aa43d563648ca79dd4f3 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 13:11:13 -0600 Subject: [PATCH 16/41] await promise --- .../test/scout/ui/tests/index_template_wizard.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 05096f27de34c..8bc303dd25c7d 100644 --- 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 @@ -209,7 +209,7 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () await pageObjects.indexManagement.clickNextButton(); // Click preview tab - page.testSubj.locator('previewTab').click(); + await page.testSubj.locator('previewTab').click(); const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); expect(templatePreview).not.toContain('error'); From 30f5383781e1ab99d37f88e2898d51322bff8f66 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 14:33:38 -0600 Subject: [PATCH 17/41] fix jest test --- .../home/index_templates_tab.helpers.ts | 5 +++-- .../home/index_templates_tab.test.ts | 12 ++++++++++-- .../template_details/template_details_content.tsx | 10 +++++----- 3 files changed, 18 insertions(+), 9 deletions(-) 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..abf985a3529e1 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,11 @@ const createActions = (testBed: TestBed) => { const selectDetailsTab = async ( tab: 'summary' | 'settings' | 'mappings' | 'aliases' | 'preview' ) => { - const tabs = ['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..2729b5b9f31c5 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 @@ -563,6 +563,7 @@ describe('Index Templates tab', () => { expect(find('templateDetails.title').text().trim()).toEqual(name); }); + // todo it('should have a close button and be able to close flyout', async () => { const { actions, component, exists } = testBed; @@ -624,6 +625,7 @@ 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', @@ -632,6 +634,13 @@ describe('Index Templates tab', () => { '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 +680,7 @@ describe('Index Templates tab', () => { isLegacy: true, }); - const { actions, find, exists } = testBed; + const { actions, exists } = testBed; httpRequestsMockHelpers.setLoadTemplateResponse( templates[0].name, @@ -679,7 +688,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/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 e1376cb50c2fc..01db4011555d2 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,35 +55,35 @@ const TABS = [ name: i18n.translate('xpack.idxMgmt.templateDetails.summaryTabTitle', { defaultMessage: 'Summary', }), - dataTestSubj: 'summaryTab', + dataTestSubj: 'summaryTabBtn', }, { id: SETTINGS_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.settingsTabTitle', { defaultMessage: 'Settings', }), - dataTestSubj: 'settingsTab', + dataTestSubj: 'settingsTabBtn', }, { id: MAPPINGS_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.mappingsTabTitle', { defaultMessage: 'Mappings', }), - dataTestSubj: 'mappingsTab', + dataTestSubj: 'mappingsTabBtn', }, { id: ALIASES_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.aliasesTabTitle', { defaultMessage: 'Aliases', }), - dataTestSubj: 'aliasesTab', + dataTestSubj: 'aliasesTabBtn', }, { id: PREVIEW_TAB_ID, name: i18n.translate('xpack.idxMgmt.templateDetails.previewTabTitle', { defaultMessage: 'Preview', }), - dataTestSubj: 'previewTab', + dataTestSubj: 'previewTabBtn', }, ]; From c43760d0bc7739901a1e64338a92629b7cd1e91d Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 14:47:29 -0600 Subject: [PATCH 18/41] type fix --- .../client_integration/helpers/test_subjects.ts | 7 ++++++- .../home/index_templates_tab.test.ts | 12 ------------ .../test/scout/ui/tests/home_page.spec.ts | 6 +++--- 3 files changed, 9 insertions(+), 16 deletions(-) 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 0862977d3bf2e..40f7bba2b4fb6 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 @@ -118,4 +118,9 @@ export type TestSubjects = | 'noIndicesMessage' | 'clearIndicesSearch' | 'usingMaxRetention' - | 'componentTemplatesLink'; + | 'componentTemplatesLink' + | 'summaryTabBtn' + | 'settingsTabBtn' + | 'mappingsTabBtn' + | 'aliasesTabBtn' + | 'previewTabBtn'; 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 2729b5b9f31c5..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 @@ -563,7 +563,6 @@ describe('Index Templates tab', () => { expect(find('templateDetails.title').text().trim()).toEqual(name); }); - // todo it('should have a close button and be able to close flyout', async () => { const { actions, component, exists } = testBed; @@ -625,17 +624,6 @@ 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); 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 index 890e0556e6209..c5c9998527a3a 100644 --- 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 @@ -7,12 +7,12 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; -// import { CUSTOM_ROLES } from './custom_roles'; +import { CUSTOM_ROLES } from './custom_roles'; test.describe('Home page', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); - await browserAuth.loginAsAdmin(); + await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); + // await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.goto(); }); From 12b0f03012a7b42dc5925c614c0449c65574e45b Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 17:59:00 -0600 Subject: [PATCH 19/41] scout test fixes --- .../test/scout/ui/fixtures/index.ts | 70 ++++++++++++++++++- .../page_objects/index_management_page.ts | 6 +- .../test/scout/ui/tests/custom_roles.ts | 2 +- .../test/scout/ui/tests/home_page.spec.ts | 6 +- .../ui/tests/index_template_wizard.spec.ts | 23 +++--- 5 files changed, 88 insertions(+), 19 deletions(-) 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 index 7f79274779652..77aa5168c5294 100644 --- 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 @@ -7,10 +7,30 @@ import { test as base } from '@kbn/scout'; import type { ScoutPage, ScoutTestFixtures, ScoutWorkerFixtures } 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 type { IndexManagementPageObjects } from './page_objects'; -import { extendPageObjects } from './page_objects'; +// import type { IndexManagementPageObjects } from './page_objects'; +// import { extendPageObjects } from './page_objects'; +/* +interface IndexManagementPO { + indexManagement: IndexManagement; +} + +export type IndexManagementPageObjects = IndexManagementPO & PageObjects; + +export function extendPageObjects( + pageObjects: PageObjects, + page: ScoutPage +): IndexManagementPageObjects { + return { + ...pageObjects, + indexManagement: createLazyPageObject(IndexManagement, page), + }; +} export interface ConsoleTestFixtures extends ScoutTestFixtures { pageObjects: IndexManagementPageObjects; } @@ -30,3 +50,49 @@ export const test = base.extend({ await use(extendedPageObjects); }, }); +*/ + +type PageObjectClass = new (page: ScoutPage) => AbstractPageObject; + +export const createTest = function >( + pageObjectClassMap: Record +) { + type PageObjectsExtended = PageObjectsExtensions & PageObjects; + interface TestFixtures extends ScoutTestFixtures { + pageObjects: PageObjectsExtended; + } + + 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({ + pageObjects: async ( + { + pageObjects, + page, + }: { + pageObjects: PageObjectsExtended; + page: ScoutPage; + }, + use: (pageObjects: PageObjectsExtended) => Promise + ) => { + const extendedPageObjects = extendPOs(pageObjects, page); + await use(extendedPageObjects); + }, + }); +}; + +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_management_page.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/page_objects/index_management_page.ts index 2f8e7ec8ebb9e..fa18e18ef7f41 100644 --- 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 @@ -5,11 +5,15 @@ * 2.0. */ +/* eslint-disable max-classes-per-file */ + import type { ScoutPage } from '@kbn/scout'; -export class IndexManagement { +export class AbstractPageObject { constructor(public readonly page: ScoutPage) {} +} +export class IndexManagement extends AbstractPageObject { async goto() { await this.page.gotoApp('management/data/index_management'); } diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts index 185059698a786..2ee06c7196760 100644 --- a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts @@ -11,7 +11,7 @@ export const CUSTOM_ROLES: Record = { indexManagement: { elasticsearch: { // would be nice if this wasn't needed - cluster: ['monitor'], + cluster: ['manage'], indices: [ { names: ['*'], 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 index c5c9998527a3a..890e0556e6209 100644 --- 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 @@ -7,12 +7,12 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; -import { CUSTOM_ROLES } from './custom_roles'; +// import { CUSTOM_ROLES } from './custom_roles'; test.describe('Home page', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); - // await browserAuth.loginAsAdmin(); + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); + await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.goto(); }); 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 index 8bc303dd25c7d..5972f284e05da 100644 --- 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 @@ -152,11 +152,19 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); }); + 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 preview index template that matches a_fake_index_pattern_that_wont_match_any_indices', async ({ page, pageObjects, - esClient, - log, }) => { // Click Create Template button await page.testSubj.locator('createTemplateButton').click(); @@ -209,21 +217,12 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () await pageObjects.indexManagement.clickNextButton(); // Click preview tab - await page.testSubj.locator('previewTab').click(); + 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(); - - // Cleanup - 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)}` - ); - } }); }); From e91f2c8a14ee3c533eaefb43415f8ce7658a69b1 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 20:03:59 -0600 Subject: [PATCH 20/41] type fix --- .../index_management/test/scout/ui/fixtures/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 index 77aa5168c5294..9472369f23d4f 100644 --- 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 @@ -58,9 +58,6 @@ export const createTest = function ) { type PageObjectsExtended = PageObjectsExtensions & PageObjects; - interface TestFixtures extends ScoutTestFixtures { - pageObjects: PageObjectsExtended; - } function extendPOs(pageObjects: PageObjects, page: ScoutPage): PageObjectsExtended { const initedLazyPageObjects = Object.keys(pageObjectClassMap).reduce< @@ -76,7 +73,12 @@ export const createTest = function ({ + return base.extend< + ScoutTestFixtures & { + pageObjects: PageObjectsExtended; + }, + ScoutWorkerFixtures + >({ pageObjects: async ( { pageObjects, From d43f4d5ccce1498203c95999f4ac36f98f3a10db Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 14 Nov 2025 22:32:14 -0600 Subject: [PATCH 21/41] remove comments --- .../home/index_templates_tab.helpers.ts | 1 - .../test/scout/ui/fixtures/index.ts | 40 ------------------- 2 files changed, 41 deletions(-) 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 abf985a3529e1..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,7 +40,6 @@ 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 () => { 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 index 9472369f23d4f..3545563d298b9 100644 --- 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 @@ -12,46 +12,6 @@ import { createLazyPageObject } from '@kbn/scout'; import type { AbstractPageObject } from './page_objects/index_management_page'; import { IndexManagement } from './page_objects/index_management_page'; -// import type { IndexManagementPageObjects } from './page_objects'; -// import { extendPageObjects } from './page_objects'; - -/* -interface IndexManagementPO { - indexManagement: IndexManagement; -} - -export type IndexManagementPageObjects = IndexManagementPO & PageObjects; - -export function extendPageObjects( - pageObjects: PageObjects, - page: ScoutPage -): IndexManagementPageObjects { - return { - ...pageObjects, - indexManagement: createLazyPageObject(IndexManagement, page), - }; -} -export interface ConsoleTestFixtures extends ScoutTestFixtures { - pageObjects: IndexManagementPageObjects; -} - -export const test = base.extend({ - pageObjects: async ( - { - pageObjects, - page, - }: { - pageObjects: IndexManagementPageObjects; - page: ScoutPage; - }, - use: (pageObjects: IndexManagementPageObjects) => Promise - ) => { - const extendedPageObjects = extendPageObjects(pageObjects, page); - await use(extendedPageObjects); - }, -}); -*/ - type PageObjectClass = new (page: ScoutPage) => AbstractPageObject; export const createTest = function >( From 3e325579c1916d4ab0d13c99c11549717fcfdfeb Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sun, 16 Nov 2025 20:53:49 -0600 Subject: [PATCH 22/41] improve user privs --- .../index_management/test/scout/ui/tests/custom_roles.ts | 4 ++-- .../index_management/test/scout/ui/tests/home_page.spec.ts | 2 +- .../test/scout/ui/tests/index_details_page.spec.ts | 2 +- .../test/scout/ui/tests/index_template_wizard.spec.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts index 2ee06c7196760..b77fa5f82f7c7 100644 --- a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts @@ -8,10 +8,10 @@ import type { KibanaRole } from '@kbn/scout'; export const CUSTOM_ROLES: Record = { - indexManagement: { + indexManagementUser: { elasticsearch: { // would be nice if this wasn't needed - cluster: ['manage'], + cluster: ['monitor', 'manage_index_templates', 'manage_enrich'], indices: [ { names: ['*'], 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 index 890e0556e6209..a2c33cf8011b3 100644 --- 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 @@ -11,7 +11,7 @@ import { test } from '../fixtures'; test.describe('Home page', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.goto(); 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 index e884c11586bda..a290dce2587ca 100644 --- 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 @@ -11,7 +11,7 @@ import { test } from '../fixtures'; test.describe('Index details page', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.goto(); }); 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 index 5972f284e05da..961f8d90887a7 100644 --- 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 @@ -11,7 +11,7 @@ import { test } from '../fixtures'; test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects, page }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); + // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); await browserAuth.loginAsAdmin(); await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); // Click Create Template button From 61aed88cb9154540694e23b28cd90b4f19cf2b58 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sun, 16 Nov 2025 22:16:10 -0600 Subject: [PATCH 23/41] move some logic to pageObject to be shared --- .../ui/fixtures/page_objects/index_management_page.ts | 10 +++++++++- .../test/scout/ui/tests/index_details_page.spec.ts | 11 ++--------- 2 files changed, 11 insertions(+), 10 deletions(-) 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 index fa18e18ef7f41..3e87453c5425c 100644 --- 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 @@ -7,7 +7,7 @@ /* eslint-disable max-classes-per-file */ -import type { ScoutPage } from '@kbn/scout'; +import { type ScoutPage, expect } from '@kbn/scout'; export class AbstractPageObject { constructor(public readonly page: ScoutPage) {} @@ -119,4 +119,12 @@ export class IndexManagement extends AbstractPageObject { }; 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(); + }, + }; } 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 index a290dce2587ca..77ed553e53535 100644 --- 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 @@ -5,7 +5,6 @@ * 2.0. */ -import { expect } from '@kbn/scout'; import { test } from '../fixtures'; // import { CUSTOM_ROLES } from './custom_roles'; @@ -16,11 +15,7 @@ test.describe('Index details page', { tag: ['@ess'] }, () => { await pageObjects.indexManagement.goto(); }); - test('Navigates to the index details page from the home page', async ({ - pageObjects, - log, - page, - }) => { + 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 @@ -30,8 +25,6 @@ test.describe('Index details page', { tag: ['@ess'] }, () => { await pageObjects.indexManagement.openIndexDetailsPage(0); // Verify index details page is loaded - await expect(page.testSubj.locator('indexDetailsTab-overview')).toBeVisible(); - await expect(page.testSubj.locator('indexDetailsContent')).toBeVisible(); - await expect(page.testSubj.locator('indexDetailsBackToIndicesButton')).toBeVisible(); + await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); }); }); From 77e24acc6149e58b692793846b8ba9574d7dba9b Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Sun, 16 Nov 2025 22:37:01 -0600 Subject: [PATCH 24/41] consolidate some logic into pageObject --- .../page_objects/index_management_page.ts | 13 +++++++++ .../ui/tests/index_template_wizard.spec.ts | 28 ++++--------------- 2 files changed, 19 insertions(+), 22 deletions(-) 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 index 3e87453c5425c..8e04742112bac 100644 --- 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 @@ -127,4 +127,17 @@ export class IndexManagement extends AbstractPageObject { await expect(this.page.testSubj.locator('indexDetailsBackToIndicesButton')).toBeVisible(); }, }; + + indexTemplateWizard = { + completeStepOne: async () => { + await this.page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await this.page.testSubj + .locator('indexPatternsField') + .locator('input') + .fill('test-index-pattern'); + + // Click Next button + await this.clickNextButton(); + }, + }; } 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 index 961f8d90887a7..07688bcc5c53a 100644 --- 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 @@ -38,20 +38,12 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test('renders logistics (step 1)', async ({ page, pageObjects }) => { // Verify step title await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); - - // Fill out required fields - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); }); test('renders component templates (step 2)', async ({ page, pageObjects }) => { // Fill logistics step - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); - await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); // Verify empty prompt await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); @@ -62,9 +54,7 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test('renders index settings (step 3)', async ({ page, pageObjects }) => { // Fill logistics step - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); - await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); // Skip component templates step await pageObjects.indexManagement.clickNextButton(); @@ -83,9 +73,7 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test('renders mappings (step 4)', async ({ page, pageObjects }) => { // Fill logistics step - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); - await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); // Skip component templates and settings steps await pageObjects.indexManagement.clickNextButton(); @@ -100,9 +88,7 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test('renders aliases (step 5)', async ({ page, pageObjects }) => { // Fill logistics step - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); - await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); // Skip component templates, settings, and mappings steps await pageObjects.indexManagement.clickNextButton(); @@ -118,9 +104,7 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test('renders review template (step 6)', async ({ page, pageObjects }) => { // Fill logistics step - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); - await pageObjects.indexManagement.clickNextButton(); + await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); // Skip all intermediate steps await pageObjects.indexManagement.clickNextButton(); From 2256982f3eb47b953374a9c7c3e6826f2463dfe6 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 20 Nov 2025 00:47:47 -0600 Subject: [PATCH 25/41] better auth integration --- .../test/scout/ui/fixtures/index.ts | 28 +++++++++++++++-- .../test/scout/ui/tests/custom_roles.ts | 30 ------------------- .../test/scout/ui/tests/home_page.spec.ts | 7 ++--- .../scout/ui/tests/index_details_page.spec.ts | 6 ++-- .../ui/tests/index_template_wizard.spec.ts | 6 ++-- 5 files changed, 32 insertions(+), 45 deletions(-) delete mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts 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 index 3545563d298b9..1b7b0ab8e4348 100644 --- 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 @@ -6,14 +6,24 @@ */ import { test as base } from '@kbn/scout'; -import type { ScoutPage, ScoutTestFixtures, ScoutWorkerFixtures } 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 ) { @@ -37,7 +47,9 @@ export const createTest = function ({ pageObjects: async ( { @@ -52,6 +64,18 @@ export const createTest = function Promise + ) => { + const loginAsIndexManagementUser = async () => + browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); + + await use({ + ...browserAuth, + loginAsIndexManagementUser, + }); + }, }); }; diff --git a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts deleted file mode 100644 index b77fa5f82f7c7..0000000000000 --- a/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/custom_roles.ts +++ /dev/null @@ -1,30 +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 { 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/tests/home_page.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/home_page.spec.ts index a2c33cf8011b3..cd3c08f38baaa 100644 --- 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 @@ -7,13 +7,10 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; -// import { CUSTOM_ROLES } from './custom_roles'; test.describe('Home page', { tag: ['@ess'] }, () => { - test.beforeEach(async ({ browserAuth, pageObjects }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); - await browserAuth.loginAsAdmin(); - + test.beforeEach(async ({ pageObjects, browserAuth }) => { + await browserAuth.loginAsIndexManagementUser(); await pageObjects.indexManagement.goto(); }); 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 index 77ed553e53535..1ddd2dc68aadb 100644 --- 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 @@ -6,12 +6,10 @@ */ import { test } from '../fixtures'; -// import { CUSTOM_ROLES } from './custom_roles'; test.describe('Index details page', { tag: ['@ess'] }, () => { - test.beforeEach(async ({ browserAuth, pageObjects }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); - await browserAuth.loginAsAdmin(); + test.beforeEach(async ({ pageObjects, browserAuth }) => { + await browserAuth.loginAsIndexManagementUser(); await pageObjects.indexManagement.goto(); }); 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 index 07688bcc5c53a..654cf29d5d91c 100644 --- 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 @@ -7,12 +7,10 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; -// import { CUSTOM_ROLES } from './custom_roles'; test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { - test.beforeEach(async ({ browserAuth, pageObjects, page }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagementUser); - await browserAuth.loginAsAdmin(); + test.beforeEach(async ({ pageObjects, page, browserAuth }) => { + await browserAuth.loginAsIndexManagementUser(); await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); // Click Create Template button await page.testSubj.locator('createTemplateButton').click(); From a4c85c0e00e6d38c9d70efd45b46248009bea268 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 20 Nov 2025 00:55:25 -0600 Subject: [PATCH 26/41] add custom role file --- .../test/scout/ui/fixtures/custom_roles.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/fixtures/custom_roles.ts 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: ['*'], + }, + ], + }, +}; From 4599facf23803eaaca80ccfc74e6abe27f50e116 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 25 Nov 2025 03:02:17 +0000 Subject: [PATCH 27/41] Changes from node scripts/regenerate_moon_projects.js --update --- x-pack/platform/packages/shared/kbn-evals/moon.yml | 1 + .../packages/shared/onechat/kbn-evals-suite-onechat/moon.yml | 1 + x-pack/platform/plugins/shared/index_management/moon.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/x-pack/platform/packages/shared/kbn-evals/moon.yml b/x-pack/platform/packages/shared/kbn-evals/moon.yml index 446644cf07d57..4275a7a991462 100644 --- a/x-pack/platform/packages/shared/kbn-evals/moon.yml +++ b/x-pack/platform/packages/shared/kbn-evals/moon.yml @@ -33,6 +33,7 @@ dependsOn: - '@kbn/std' - '@kbn/test' - '@kbn/inference-prompt-utils' + - '@kbn/tracing-utils' tags: - test-helper - package diff --git a/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/moon.yml b/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/moon.yml index 8c7fabec4b6a9..349a6078823af 100644 --- a/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/moon.yml +++ b/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/moon.yml @@ -22,6 +22,7 @@ dependsOn: - '@kbn/onechat-common' - '@kbn/tooling-log' - '@kbn/core' + - '@kbn/scout' tags: - functional-tests - package 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 From 92f6cbb833aad55dbc088f8871a53e1bf75fe4a0 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 24 Nov 2025 22:28:17 -0600 Subject: [PATCH 28/41] use correct user for tests --- .../test/scout/ui/tests/index_template_wizard.spec.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 index 654cf29d5d91c..0484b5b610a05 100644 --- 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 @@ -129,8 +129,7 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); - await browserAuth.loginAsAdmin(); + await browserAuth.loginAsIndexManagementUser(); await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); }); @@ -210,8 +209,7 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => { test.beforeEach(async ({ browserAuth, pageObjects, page }) => { - // await browserAuth.loginWithCustomRole(CUSTOM_ROLES.indexManagement); - await browserAuth.loginAsAdmin(); + await browserAuth.loginAsIndexManagementUser(); await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); // Click Create Template button From c75d6aa0f00a11e01d04ff45086bc973d6b61c71 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 25 Nov 2025 00:28:05 -0600 Subject: [PATCH 29/41] fix FTR tests --- .../index_management/index_templates_tab/index_template_tab.ts | 3 +-- .../test_suites/management/index_management/index_templates.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) 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..c00e2d6852f51 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('fieldsTab'); // Modify timestamp format await testSubjects.click('comboBoxClearButton'); 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..8a3daa144b9c3 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('fieldsTab'); // Modify timestamp format await testSubjects.click('comboBoxClearButton'); From fbb31c422fad72f60ea0ee5df3cb9022b8e83475 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 26 Nov 2025 00:46:47 -0600 Subject: [PATCH 30/41] fix FTR, improve scout tests --- .../page_objects/index_management_page.ts | 16 +-- .../test/scout/ui/tests/home_page.spec.ts | 23 ++-- .../ui/tests/index_template_wizard.spec.ts | 108 ---------------- .../index_template_wizard_mappings.spec.ts | 117 ++++++++++++++++++ .../index_templates_tab/index_template_tab.ts | 6 +- .../index_management/index_templates.ts | 6 +- 6 files changed, 137 insertions(+), 139 deletions(-) create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_mappings.spec.ts 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 index 8e04742112bac..b140735291aac 100644 --- 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 @@ -38,31 +38,23 @@ export class IndexManagement extends AbstractPageObject { } async setCreateIndexName(value: string) { - const nameField = this.page.testSubj.locator('createIndexNameFieldText'); - await nameField.waitFor({ state: 'visible' }); - await nameField.fill(value); + await this.page.testSubj.fill('createIndexNameFieldText', value); } async setCreateIndexMode(value: string) { - const modeField = this.page.testSubj.locator('indexModeField'); - await modeField.waitFor({ state: 'visible' }); - await modeField.click(); + 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.waitFor({ state: 'visible' }); await saveButton.click(); // Wait for modal to close await saveButton.waitFor({ state: 'hidden', timeout: 30000 }); } - async expectIndexToExist(indexName: string) { - // Wait for the table to be visible - const table = this.page.locator('table'); - await table.waitFor({ state: 'visible' }); - this.page.getByText(indexName); + async indexLinkVisible(indexName: string) { + return this.page.getByRole('button').getByText(indexName).isVisible(); } async toggleHiddenIndices() { 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 index cd3c08f38baaa..695a192612625 100644 --- 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 @@ -8,12 +8,22 @@ 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 }) => { + 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, @@ -43,21 +53,12 @@ test.describe('Home page', { tag: ['@ess'] }, () => { await expect(page.testSubj.locator('indexTable')).toBeVisible(); }); - test('Indices - can create an index', async ({ pageObjects, esClient, log }) => { - const testIndexName = `index-test-${Math.random()}`; - + 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 pageObjects.indexManagement.expectIndexToExist(testIndexName); - - // Cleanup - try { - await esClient.indices.delete({ index: testIndexName }); - } catch (e: any) { - log.debug(`Index cleanup failed for ${testIndexName}: ${e.message}`); - } + expect(await pageObjects.indexManagement.indexLinkVisible(testIndexName)).toBe(true); }); test('Data streams - renders the data streams tab', async ({ pageObjects, page }) => { 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 index 0484b5b610a05..210eed13ff61e 100644 --- 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 @@ -206,111 +206,3 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () await page.testSubj.locator('closeDetailsButton').click(); }); }); - -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 - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').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, - }) => { - // Add a mapping field - // 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, - }) => { - // Add a mapping field - // 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_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..b67d9efa86383 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_mappings.spec.ts @@ -0,0 +1,117 @@ +/* + * 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 - 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 + await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); + await page.testSubj.locator('indexPatternsField').locator('input').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, + }) => { + // Add a mapping field + // 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, + }) => { + // Add a mapping field + // 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/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 c00e2d6852f51..3c941de2b4ded 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 @@ -158,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'); @@ -179,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'); 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 8a3daa144b9c3..33abe6f2c145e 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 @@ -194,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'); @@ -215,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'); From f0675461ab27f0c3da025f11c1c622bd3436580b Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 26 Nov 2025 01:16:14 -0600 Subject: [PATCH 31/41] type fix --- .../index_management/test/scout/ui/tests/home_page.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 695a192612625..769c87c68b47c 100644 --- 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 @@ -16,7 +16,7 @@ test.describe('Home page', { tag: ['@ess'] }, () => { await pageObjects.indexManagement.goto(); }); - test.afterAll(async ({ esClient }) => { + test.afterAll(async ({ esClient, log }) => { try { await esClient.indices.delete({ index: testIndexName }); } catch (e: any) { From 40a24de6f1961447c77284b0cb13abad52b91fdb Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 26 Nov 2025 01:28:34 -0600 Subject: [PATCH 32/41] scout test fixes --- .../page_objects/index_management_page.ts | 1 - .../ui/tests/index_template_wizard.spec.ts | 80 ----------------- .../index_template_wizard_preview.spec.ts | 89 +++++++++++++++++++ 3 files changed, 89 insertions(+), 81 deletions(-) create mode 100644 x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_preview.spec.ts 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 index b140735291aac..7ad538df25fb1 100644 --- 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 @@ -128,7 +128,6 @@ export class IndexManagement extends AbstractPageObject { .locator('input') .fill('test-index-pattern'); - // Click Next button await this.clickNextButton(); }, }; 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 index 210eed13ff61e..b8a80504f2ba0 100644 --- 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 @@ -126,83 +126,3 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { await pageObjects.indexManagement.clickNextButton(); }); }); - -test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () => { - test.beforeEach(async ({ browserAuth, pageObjects }) => { - await browserAuth.loginAsIndexManagementUser(); - await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - }); - - 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 preview index template that matches a_fake_index_pattern_that_wont_match_any_indices', async ({ - page, - pageObjects, - }) => { - // Click Create Template button - await page.testSubj.locator('createTemplateButton').click(); - await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); - await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); - - // Fill out required fields - await page.testSubj.locator('nameField').locator('input').fill('a-star'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('a*'); - await page.testSubj.locator('priorityField').locator('input').fill('1000'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify empty prompt - await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText("Review details for 'a-star'"); - - // Verify that summary exists - await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); - - // Verify that index mode is set to "Standard" - await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); - await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); - - // Click Create template - await pageObjects.indexManagement.clickNextButton(); - - // Click preview tab - 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/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..2bc229a76cfd5 --- /dev/null +++ b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_preview.spec.ts @@ -0,0 +1,89 @@ +/* + * 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 - Preview template', { tag: ['@ess'] }, () => { + test.beforeEach(async ({ browserAuth, pageObjects }) => { + await browserAuth.loginAsIndexManagementUser(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); + }); + + 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 preview index template that matches a_fake_index_pattern_that_wont_match_any_indices', async ({ + page, + pageObjects, + }) => { + // Click Create Template button + await page.testSubj.locator('createTemplateButton').click(); + await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); + await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); + + // Fill out required fields + await page.testSubj.locator('nameField').locator('input').fill('a-star'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('a*'); + await page.testSubj.locator('priorityField').locator('input').fill('1000'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify empty prompt + await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); + + // Click Next button + await pageObjects.indexManagement.clickNextButton(); + + // Verify step title + await expect(page.testSubj.locator('stepTitle')).toHaveText("Review details for 'a-star'"); + + // Verify that summary exists + await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); + + // Verify that index mode is set to "Standard" + await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); + await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); + + // Click Create template + await pageObjects.indexManagement.clickNextButton(); + + // Click preview tab + 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(); + }); +}); From 0ec2588d49c7c4a5e08f4d4a66b007e357a6d1a0 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 26 Nov 2025 13:52:43 -0600 Subject: [PATCH 33/41] fix scout test --- .../ui/fixtures/page_objects/index_management_page.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index 7ad538df25fb1..31d23428c6171 100644 --- 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 @@ -54,7 +54,9 @@ export class IndexManagement extends AbstractPageObject { } async indexLinkVisible(indexName: string) { - return this.page.getByRole('button').getByText(indexName).isVisible(); + const indexButton = this.page.getByRole('button').getByText(indexName); + await indexButton.waitFor({ state: 'visible', timeout: 30000 }); // necessary since this can be slow in CI + return indexButton.isVisible(); } async toggleHiddenIndices() { @@ -98,9 +100,7 @@ export class IndexManagement extends AbstractPageObject { await input.fill(value); // Wait for and click the option - const option = this.page.locator(`[role="option"]`).filter({ hasText: value }); - await option.waitFor({ state: 'visible' }); - await option.click(); + await this.page.locator(`[role="option"]`).filter({ hasText: value }).click(); } async changeMappingsEditorTab(tab: 'fields' | 'advancedOptions' | 'templates') { From ec76d4a6a3afb66c798d6aa4f22913f79fa6e697 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 26 Nov 2025 14:35:58 -0600 Subject: [PATCH 34/41] type fix, scout test fix --- .../__jest__/client_integration/helpers/test_subjects.ts | 2 +- .../test/scout/ui/tests/index_template_wizard_mappings.spec.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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 60edb5ba55a2a..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 @@ -123,5 +123,5 @@ export type TestSubjects = | 'settingsTabBtn' | 'mappingsTabBtn' | 'aliasesTabBtn' - | 'previewTabBtn'; + | 'previewTabBtn' | 'configureFailureStoreButton'; 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 index b67d9efa86383..b3d3cba8788cd 100644 --- 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 @@ -40,6 +40,7 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, }) => { // Add a mapping field + await page.testSubj.locator('addFieldButton').click(); // Select Numeric type await pageObjects.indexManagement.setComboBox('fieldType', 'Numeric'); @@ -59,6 +60,7 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, }) => { // Add a mapping field + await page.testSubj.locator('addFieldButton').click(); // Select Range type await pageObjects.indexManagement.setComboBox('fieldType', 'Range'); From cb982b538da07e268202765a1d64821b7d57e2b8 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 26 Nov 2025 16:25:40 -0600 Subject: [PATCH 35/41] fix add button ref --- .../scout/ui/tests/index_template_wizard_mappings.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 index b3d3cba8788cd..baffeb22dbdb7 100644 --- 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 @@ -40,7 +40,8 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, }) => { // Add a mapping field - await page.testSubj.locator('addFieldButton').click(); + // await page.pause(); + await page.testSubj.locator('addButton').click(); // Select Numeric type await pageObjects.indexManagement.setComboBox('fieldType', 'Numeric'); @@ -60,7 +61,7 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => pageObjects, }) => { // Add a mapping field - await page.testSubj.locator('addFieldButton').click(); + await page.testSubj.locator('addButton').click(); // Select Range type await pageObjects.indexManagement.setComboBox('fieldType', 'Range'); From eca0e255df849cb1998fe28b05b50825cceaf250 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 26 Nov 2025 20:56:45 -0600 Subject: [PATCH 36/41] fix functional test --- .../index_management/index_templates_tab/index_template_tab.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3c941de2b4ded..a6316ed206049 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,7 +140,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Navigate to Mappings await testSubjects.click('formWizardStep-3'); await pageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.click('fieldsTab'); + await testSubjects.click('advancedOptionsTab'); // Modify timestamp format await testSubjects.click('comboBoxClearButton'); From adbf566fe7624cf1cf618d1e527d55444c7e7bda Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 27 Nov 2025 00:42:48 -0600 Subject: [PATCH 37/41] test fixes --- .../index_management/index_templates_tab/index_template_tab.ts | 3 +-- .../test_suites/management/index_management/index_templates.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) 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 a6316ed206049..4f9ac204dd370 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 @@ -194,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 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 33abe6f2c145e..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,7 +176,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Navigate to Mappings await testSubjects.click('formWizardStep-3'); await pageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.click('fieldsTab'); + await testSubjects.click('advancedOptionsTab'); // Modify timestamp format await testSubjects.click('comboBoxClearButton'); From c950dc4137f3b73f3ca282bd7813cbba0351b1af Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 27 Nov 2025 01:41:27 -0600 Subject: [PATCH 38/41] add missing await --- .../index_management/index_templates_tab/index_template_tab.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4f9ac204dd370..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 @@ -194,7 +194,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Navigate to Mappings await testSubjects.click('formWizardStep-3'); await pageObjects.header.waitUntilLoadingHasFinished(); - (await testSubjects.find('advancedOptionsTab')).click(); + await (await testSubjects.find('advancedOptionsTab')).click(); // Modify source await testSubjects.click('sourceValueField'); From 21af96e3c823ce440a5ce261dd27ff943f17a401 Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Mon, 1 Dec 2025 17:48:30 +0100 Subject: [PATCH 39/41] fix: remove debug comment and clarify test behavior - Removed commented-out page.pause() debug code - Added clarifying comment explaining why addButton click was removed: the Create Field form is already open by default when mappings are empty --- .../scout/ui/tests/index_template_wizard_mappings.spec.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 index baffeb22dbdb7..8f198e41bd6c4 100644 --- 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 @@ -39,9 +39,7 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => page, pageObjects, }) => { - // Add a mapping field - // await page.pause(); - await page.testSubj.locator('addButton').click(); + // The Create Field form is already open by default when mappings are empty // Select Numeric type await pageObjects.indexManagement.setComboBox('fieldType', 'Numeric'); @@ -60,8 +58,7 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => page, pageObjects, }) => { - // Add a mapping field - await page.testSubj.locator('addButton').click(); + // The Create Field form is already open by default when mappings are empty // Select Range type await pageObjects.indexManagement.setComboBox('fieldType', 'Range'); From e7e4ab0df5770800c190190ec435cd00b4d1051d Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Tue, 2 Dec 2025 14:07:47 +0100 Subject: [PATCH 40/41] refactor: use test.step for wizard tests and EuiFieldTextWrapper - Refactor index_template_wizard.spec.ts to use test.step instead of separate test blocks for better efficiency and failure reporting - Refactor index_template_wizard_preview.spec.ts to use test.step for improved readability - Use EuiFieldTextWrapper in indexTemplateWizard.completeStepOne --- .../page_objects/index_management_page.ts | 14 +- .../ui/tests/index_template_wizard.spec.ts | 152 ++++++------------ .../index_template_wizard_preview.spec.ts | 113 ++++++------- 3 files changed, 106 insertions(+), 173 deletions(-) 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 index 31d23428c6171..c963ee486d4e7 100644 --- 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 @@ -7,7 +7,7 @@ /* eslint-disable max-classes-per-file */ -import { type ScoutPage, expect } from '@kbn/scout'; +import { type ScoutPage, expect, EuiFieldTextWrapper } from '@kbn/scout'; export class AbstractPageObject { constructor(public readonly page: ScoutPage) {} @@ -122,11 +122,13 @@ export class IndexManagement extends AbstractPageObject { indexTemplateWizard = { completeStepOne: async () => { - await this.page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await this.page.testSubj - .locator('indexPatternsField') - .locator('input') - .fill('test-index-pattern'); + 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/tests/index_template_wizard.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard.spec.ts index b8a80504f2ba0..8fed11f25349a 100644 --- 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 @@ -9,13 +9,6 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { - test.beforeEach(async ({ pageObjects, page, browserAuth }) => { - await browserAuth.loginAsIndexManagementUser(); - await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - // Click Create Template button - await page.testSubj.locator('createTemplateButton').click(); - }); - test.afterEach(async ({ esClient, log }) => { try { await esClient.indices.deleteIndexTemplate({ name: 'test-index-template' }); @@ -28,101 +21,56 @@ test.describe('Index template wizard - Create', { tag: ['@ess'] }, () => { } }); - test('should set the correct page title', async ({ page }) => { - await expect(page.testSubj.locator('pageTitle')).toBeVisible(); - await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); - }); - - test('renders logistics (step 1)', async ({ page, pageObjects }) => { - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); - await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); - }); - - test('renders component templates (step 2)', async ({ page, pageObjects }) => { - // Fill logistics step - await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); - - // Verify empty prompt - await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - test('renders index settings (step 3)', async ({ page, pageObjects }) => { - // Fill logistics step - await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); - - // Skip component templates step - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); - - // Verify that index mode callout is displayed - 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.' - ); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - test('renders mappings (step 4)', async ({ page, pageObjects }) => { - // Fill logistics step - await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); - - // Skip component templates and settings steps - await pageObjects.indexManagement.clickNextButton(); - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - test('renders aliases (step 5)', async ({ page, pageObjects }) => { - // Fill logistics step - await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); - - // Skip component templates, settings, and mappings steps - await pageObjects.indexManagement.clickNextButton(); - await pageObjects.indexManagement.clickNextButton(); - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - }); - - test('renders review template (step 6)', async ({ page, pageObjects }) => { - // Fill logistics step - await pageObjects.indexManagement.indexTemplateWizard.completeStepOne(); - - // Skip all intermediate steps - await pageObjects.indexManagement.clickNextButton(); - await pageObjects.indexManagement.clickNextButton(); - await pageObjects.indexManagement.clickNextButton(); - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText( - "Review details for 'test-index-template'" - ); - - // Verify that summary exists - await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); - - // Verify that index mode is set to "Standard" - await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); - await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); + 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(); - // Click Create template - await pageObjects.indexManagement.clickNextButton(); + 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_preview.spec.ts b/x-pack/platform/plugins/shared/index_management/test/scout/ui/tests/index_template_wizard_preview.spec.ts index 2bc229a76cfd5..abb428765bc31 100644 --- 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 @@ -9,11 +9,6 @@ import { expect } from '@kbn/scout'; import { test } from '../fixtures'; test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () => { - test.beforeEach(async ({ browserAuth, pageObjects }) => { - await browserAuth.loginAsIndexManagementUser(); - await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - }); - test.afterEach(async ({ esClient, log }) => { try { await esClient.indices.deleteIndexTemplate({ name: 'a-star' }); @@ -24,66 +19,54 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () } }); - test('can preview index template that matches a_fake_index_pattern_that_wont_match_any_indices', async ({ - page, - pageObjects, - }) => { - // Click Create Template button - await page.testSubj.locator('createTemplateButton').click(); - await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); - await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); - - // Fill out required fields - await page.testSubj.locator('nameField').locator('input').fill('a-star'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('a*'); - await page.testSubj.locator('priorityField').locator('input').fill('1000'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify empty prompt - await expect(page.testSubj.locator('emptyPrompt')).toBeVisible(); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Index settings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Mappings (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText('Aliases (optional)'); - - // Click Next button - await pageObjects.indexManagement.clickNextButton(); - - // Verify step title - await expect(page.testSubj.locator('stepTitle')).toHaveText("Review details for 'a-star'"); - - // Verify that summary exists - await expect(page.testSubj.locator('summaryTabContent')).toBeVisible(); - - // Verify that index mode is set to "Standard" - await expect(page.testSubj.locator('indexModeTitle')).toBeVisible(); - await expect(page.testSubj.locator('indexModeValue')).toHaveText('Standard'); - - // Click Create template - await pageObjects.indexManagement.clickNextButton(); - - // Click preview tab - await page.testSubj.locator('previewTabBtn').click(); - - const templatePreview = await page.testSubj.locator('simulateTemplatePreview').textContent(); - expect(templatePreview).not.toContain('error'); + test('can create and preview an index template', async ({ page, pageObjects, browserAuth }) => { + await browserAuth.loginAsIndexManagementUser(); + await pageObjects.indexManagement.navigateToIndexManagementTab('templates'); - await page.testSubj.locator('closeDetailsButton').click(); + 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'); + + await page.testSubj.locator('nameField').locator('input').fill('a-star'); + await page.testSubj.locator('indexPatternsField').locator('input').fill('a*'); + await page.testSubj.locator('priorityField').locator('input').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(); + }); }); }); From 968d9f426f72f559ddffd55f6503498f881f7da7 Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Tue, 2 Dec 2025 15:58:26 +0100 Subject: [PATCH 41/41] refactor: use web-first assertions and EUI wrappers per best practices - Replace waitFor({ state: 'hidden' }) with expect().toBeHidden() - Replace waitFor({ state: 'visible' }) with expect().toBeVisible() - Change indexLinkVisible to indexLink returning locator for caller assertions - Update test to use web-first assertion for index visibility check - Use EuiFieldTextWrapper for text input fields in mappings and preview tests --- .../page_objects/index_management_page.ts | 25 +++++++++++++------ .../test/scout/ui/tests/home_page.spec.ts | 4 ++- .../index_template_wizard_mappings.spec.ts | 12 ++++++--- .../index_template_wizard_preview.spec.ts | 13 +++++++--- 4 files changed, 37 insertions(+), 17 deletions(-) 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 index c963ee486d4e7..689ee99c10a78 100644 --- 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 @@ -49,14 +49,17 @@ export class IndexManagement extends AbstractPageObject { async clickCreateIndexSaveButton() { const saveButton = this.page.testSubj.locator('createIndexSaveButton'); await saveButton.click(); - // Wait for modal to close - await saveButton.waitFor({ state: 'hidden', timeout: 30000 }); + // Wait for modal to close using web-first assertion + await expect(saveButton).toBeHidden({ timeout: 30000 }); } - async indexLinkVisible(indexName: string) { - const indexButton = this.page.getByRole('button').getByText(indexName); - await indexButton.waitFor({ state: 'visible', timeout: 30000 }); // necessary since this can be slow in CI - return indexButton.isVisible(); + /** + * 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() { @@ -69,8 +72,8 @@ export class IndexManagement extends AbstractPageObject { // eslint-disable-next-line playwright/no-nth-methods await indexLinks.nth(indexOfRow).click(); - // Wait for index details page to load - await this.page.testSubj.locator('indexDetailsHeader').waitFor({ state: 'visible' }); + // Wait for index details page to load using web-first assertion + await expect(this.page.testSubj.locator('indexDetailsHeader')).toBeVisible(); } async navigateToIndexManagementTab( @@ -91,6 +94,12 @@ export class IndexManagement extends AbstractPageObject { 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(); 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 index 769c87c68b47c..49a02452ce3f4 100644 --- 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 @@ -58,7 +58,9 @@ test.describe('Home page', { tag: ['@ess'] }, () => { await pageObjects.indexManagement.setCreateIndexName(testIndexName); await pageObjects.indexManagement.setCreateIndexMode('Lookup'); await pageObjects.indexManagement.clickCreateIndexSaveButton(); - expect(await pageObjects.indexManagement.indexLinkVisible(testIndexName)).toBe(true); + await expect(pageObjects.indexManagement.indexLink(testIndexName)).toBeVisible({ + timeout: 30000, + }); }); test('Data streams - renders the data streams tab', async ({ pageObjects, page }) => { 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 index 8f198e41bd6c4..150bda219b6a2 100644 --- 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 @@ -5,7 +5,7 @@ * 2.0. */ -import { expect } from '@kbn/scout'; +import { expect, EuiFieldTextWrapper } from '@kbn/scout'; import { test } from '../fixtures'; test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => { @@ -16,9 +16,13 @@ test.describe('Index template wizard - Mappings step', { tag: ['@ess'] }, () => // Click Create Template button await page.testSubj.locator('createTemplateButton').click(); - // Fill out required fields - await page.testSubj.locator('nameField').locator('input').fill('test-index-template'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('test-index-pattern'); + // 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(); 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 index abb428765bc31..d484a3056cb44 100644 --- 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 @@ -5,7 +5,7 @@ * 2.0. */ -import { expect } from '@kbn/scout'; +import { expect, EuiFieldTextWrapper } from '@kbn/scout'; import { test } from '../fixtures'; test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () => { @@ -28,9 +28,14 @@ test.describe('Index template wizard - Preview template', { tag: ['@ess'] }, () await expect(page.testSubj.locator('pageTitle')).toHaveText('Create template'); await expect(page.testSubj.locator('stepTitle')).toHaveText('Logistics'); - await page.testSubj.locator('nameField').locator('input').fill('a-star'); - await page.testSubj.locator('indexPatternsField').locator('input').fill('a*'); - await page.testSubj.locator('priorityField').locator('input').fill('1000'); + 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(); });