From 342d3d7addd33de6bf8f141f229b37fc9f143713 Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Thu, 30 Apr 2026 11:53:12 +0200 Subject: [PATCH 01/14] Migrate more filed_editor unit tests to RTL --- .../confirmation_modal.test.tsx.snap | 39 ---------- .../confirmation_modal.test.tsx | 8 +- .../header/__snapshots__/header.test.tsx.snap | 25 ------- .../components/header/header.test.tsx | 17 ++++- .../field_format_editor.test.tsx.snap | 57 -------------- .../field_format_editor.test.tsx | 31 ++++---- .../disabled_call_out.test.tsx.snap | 30 -------- .../warning_call_out.test.tsx.snap | 75 ------------------- .../disabled_call_out.test.tsx | 22 ++++-- .../warning_call_out.test.tsx | 46 ++++++++---- 10 files changed, 79 insertions(+), 271 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/__snapshots__/header.test.tsx.snap delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/__snapshots__/field_format_editor.test.tsx.snap delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.tsx.snap delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap deleted file mode 100644 index d3181121b75e3..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap +++ /dev/null @@ -1,39 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Header should render normally 1`] = ` - - } - confirmButtonText={ - - } - defaultFocusedButton="confirm" - onCancel={[Function]} - onConfirm={[Function]} - title={ - - } - titleProps={ - Object { - "id": "generated-id", - } - } -/> -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx index 7296a77b50691..a32281307ef5c 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx @@ -8,13 +8,13 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; - import { DeleteFilterConfirmationModal } from './confirmation_modal'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; describe('Header', () => { test('should render normally', () => { - const component = shallow( + renderWithI18n( {}} @@ -22,6 +22,6 @@ describe('Header', () => { /> ); - expect(component).toMatchSnapshot(); + expect(screen.getByText("Delete field filter 'test'?")).toBeVisible(); }); }); diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/__snapshots__/header.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/__snapshots__/header.test.tsx.snap deleted file mode 100644 index 97486302553f5..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/__snapshots__/header.test.tsx.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Header should render normally 1`] = ` - - -

- -

-

- -

-
- -
-`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/header.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/header.test.tsx index 83e4672cff9e4..10b6b2d9f114b 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/header.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/header/header.test.tsx @@ -8,14 +8,23 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; - import { Header } from '.'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; describe('Header', () => { test('should render normally', () => { - const component = shallow(
); + renderWithI18n(
); - expect(component).toMatchSnapshot(); + expect( + screen.getByText( + 'Field filters can be used to exclude one or more fields when fetching a document. This happens when viewing a document in the Discover app, or with a table displaying results from a Discover session in the Dashboard app. If you have documents with large or unimportant fields you may benefit from filtering those out at this lower level.' + ) + ).toBeVisible(); + expect( + screen.getByText( + 'Note that multi-fields will incorrectly appear as matches in the table below. These filters only actually apply to fields in the original source document, so matching multi-fields are not actually being filtered.' + ) + ).toBeVisible(); }); }); diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/__snapshots__/field_format_editor.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/__snapshots__/field_format_editor.test.tsx.snap deleted file mode 100644 index 9d6a385cdfcff..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/__snapshots__/field_format_editor.test.tsx.snap +++ /dev/null @@ -1,57 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FieldFormatEditor should render normally 1`] = ` - - - - - - - } - > - - - -`; - -exports[`FieldFormatEditor should render nothing if there is no editor for the format 1`] = ` - - - - - - - } - > - - - -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx index ee6d5f5bac5d1..ab2f96d00604d 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx @@ -7,31 +7,28 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { FormatEditorServiceStart } from '@kbn/data-view-field-editor-plugin/public/service'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; -import { shallow } from 'enzyme'; -import React, { PureComponent } from 'react'; +import type { FormatEditorServiceStart } from '@kbn/data-view-field-editor-plugin/public/service'; import { FieldFormatEditor } from './field_format_editor'; +import { render, screen } from '@testing-library/react'; +import React, { PureComponent } from 'react'; class TestEditor extends PureComponent { - render() { - if (this.props) { - return null; - } - return
Test editor
; - } + render = () =>
Test editor
; } +const numberFormatEditorFactory = () => Promise.resolve(TestEditor); + const formatEditors: FormatEditorServiceStart['fieldFormatEditors'] = { - getById: jest.fn( - () => () => Promise.resolve(TestEditor) + getById: jest.fn((id: string) => + id === 'number' ? numberFormatEditorFactory : undefined ) as unknown as FormatEditorServiceStart['fieldFormatEditors']['getById'], - getAll: jest.fn(), + getAll: jest.fn(() => []), }; describe('FieldFormatEditor', () => { it('should render normally', async () => { - const component = shallow( + render( { /> ); - expect(component).toMatchSnapshot(); + expect(formatEditors.getById).toHaveBeenCalledWith('number'); + expect(await screen.findByTestId('test-editor')).toBeVisible(); }); it('should render nothing if there is no editor for the format', async () => { - const component = shallow( + render( { /> ); - expect(component).toMatchSnapshot(); + expect(formatEditors.getById).toHaveBeenCalledWith('ip'); + expect(await screen.queryByTestId('test-editor')).not.toBeInTheDocument(); }); }); diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.tsx.snap deleted file mode 100644 index 21f7be4f2c091..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/disabled_call_out.test.tsx.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ScriptingDisabledCallOut should render normally 1`] = ` - - - } - > -

- -

-
- -
-`; - -exports[`ScriptingDisabledCallOut should render nothing if not visible 1`] = `""`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap deleted file mode 100644 index e654de9cd9ec1..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap +++ /dev/null @@ -1,75 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ScriptingWarningCallOut should render normally 1`] = ` -Array [ -
-

- Familiarize yourself with - - and - - before using this feature. Scripted fields can be used to display and aggregate calculated values. As such, they can be very slow and, if done incorrectly, can cause Kibana to become unusable. -

-
, -
, -
-

-

-
-
-
-

- For greater flexibility and Painless script support, use - - . -

-
-
-
, -
, -] -`; - -exports[`ScriptingWarningCallOut should render nothing if not visible 1`] = ` - -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx index 57debfc3c9690..b9efa929da5d9 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx @@ -8,20 +8,32 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; import { ScriptingDisabledCallOut } from './disabled_call_out'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; describe('ScriptingDisabledCallOut', () => { it('should render normally', async () => { - const component = shallow(); + renderWithI18n(); - expect(component).toMatchSnapshot(); + expect(screen.getByText('Scripting disabled')).toBeVisible(); + expect( + screen.getByText( + 'All inline scripting has been disabled in Elasticsearch. You must enable inline scripting for at least one language in order to use scripted fields in Kibana.' + ) + ).toBeVisible(); }); it('should render nothing if not visible', async () => { - const component = shallow(); + const { container } = renderWithI18n(); - expect(component).toMatchSnapshot(); + expect(container).toBeEmptyDOMElement(); + expect(screen.queryByText('Scripting disabled')).not.toBeInTheDocument(); + expect( + screen.queryByText( + 'All inline scripting has been disabled in Elasticsearch. You must enable inline scripting for at least one language in order to use scripted fields in Kibana.' + ) + ).not.toBeInTheDocument(); }); }); diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/warning_call_out.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/warning_call_out.test.tsx index aa3ad6636d9d7..6028e4f604445 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/warning_call_out.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/warning_call_out.test.tsx @@ -8,33 +8,47 @@ */ import React from 'react'; -import { mountWithI18nProvider } from '@kbn/test-jest-helpers'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public/context'; import { mockManagementPlugin } from '../../../../mocks'; +import { renderWithKibanaRenderContext } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; import { ScriptingWarningCallOut } from './warning_call_out'; describe('ScriptingWarningCallOut', () => { const mockedContext = mockManagementPlugin.createIndexPatternManagmentContext(); it('should render normally', async () => { - const component = mountWithI18nProvider(, { - wrappingComponent: KibanaContextProvider, - wrappingComponentProps: { - services: mockedContext, - }, - }); + renderWithKibanaRenderContext( + + + + ); - expect(component.render()).toMatchSnapshot(); + expect( + screen.getByText( + 'Scripted fields can be used to display and aggregate calculated values. As such, they can be very slow and, if done incorrectly, can cause Kibana to become unusable.', + { exact: false } + ) + ).toBeVisible(); + expect(screen.getByText('Scripted fields are deprecated')).toBeVisible(); + expect( + screen.getByText('For greater flexibility and Painless script support, use', { exact: false }) + ).toBeVisible(); }); it('should render nothing if not visible', async () => { - const component = mountWithI18nProvider(, { - wrappingComponent: KibanaContextProvider, - wrappingComponentProps: { - services: mockedContext, - }, - }); + const { container } = renderWithKibanaRenderContext( + + + + ); - expect(component).toMatchSnapshot(); + expect(container).toBeEmptyDOMElement(); + expect(screen.queryByText('Scripted fields are deprecated')).not.toBeInTheDocument(); + expect( + screen.queryByText('For greater flexibility and Painless script support, use', { + exact: false, + }) + ).not.toBeInTheDocument(); }); }); From 21949b51a19224d8473648bfa01173a1ce734c11 Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 10:46:31 +0200 Subject: [PATCH 02/14] Convert help_flyout test --- .../__snapshots__/help_flyout.test.tsx.snap | 85 ------------------- .../scripting_help/help_flyout.test.tsx | 69 ++++++++------- 2 files changed, 36 insertions(+), 118 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap deleted file mode 100644 index e7264d2f15f2f..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/__snapshots__/help_flyout.test.tsx.snap +++ /dev/null @@ -1,85 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ScriptingHelpFlyout should render normally 1`] = ` - - - , - "data-test-subj": "syntaxTab", - "id": "syntax", - "name": "Syntax", - } - } - tabs={ - Array [ - Object { - "content": , - "data-test-subj": "syntaxTab", - "id": "syntax", - "name": "Syntax", - }, - Object { - "content": , - "data-test-subj": "testTab", - "id": "test", - "name": "Preview results", - }, - ] - } - /> - - -`; - -exports[`ScriptingHelpFlyout should render nothing if not visible 1`] = ` - - - , - "data-test-subj": "syntaxTab", - "id": "syntax", - "name": "Syntax", - } - } - tabs={ - Array [ - Object { - "content": , - "data-test-subj": "syntaxTab", - "id": "syntax", - "name": "Syntax", - }, - Object { - "content": , - "data-test-subj": "testTab", - "id": "test", - "name": "Preview results", - }, - ] - } - /> - - -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/help_flyout.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/help_flyout.test.tsx index 6f8b03764e706..34194b8c93691 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/help_flyout.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_help/help_flyout.test.tsx @@ -8,48 +8,51 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; - +import { buildDataViewMock } from '@kbn/discover-utils/src/__mocks__/data_view'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen, within } from '@testing-library/react'; import { ScriptingHelpFlyout } from './help_flyout'; -import type { DataView } from '@kbn/data-views-plugin/public'; - -import type { ExecuteScript } from '../../types'; - -jest.mock('./test_script', () => ({ - TestScript: () => { - return `
mockTestScript
`; - }, +jest.mock('@kbn/kibana-react-plugin/public', () => ({ + useKibana: () => ({ + services: { + docLinks: { + links: { + scriptedFields: { + luceneExpressions: '#', + painless: '#', + painlessApi: '#', + painlessSyntax: '#', + }, + }, + }, + }, + }), })); -const indexPatternMock = {} as DataView; +const renderFlyout = (isVisible: boolean) => + renderWithI18n( + + ); describe('ScriptingHelpFlyout', () => { - it('should render normally', async () => { - const component = shallow( - {}) as unknown as ExecuteScript} - onClose={() => {}} - /> - ); + it('should render normally', () => { + renderFlyout(true); - expect(component).toMatchSnapshot(); + expect(within(screen.getByTestId('syntaxTab')).getByText('Syntax')).toBeVisible(); + expect(within(screen.getByTestId('testTab')).getByText('Preview results')).toBeVisible(); + expect(screen.getByText('Painless')).toBeVisible(); }); - it('should render nothing if not visible', async () => { - const component = shallow( - {}) as unknown as ExecuteScript} - onClose={() => {}} - /> - ); + it('should render nothing if not visible', () => { + const { container } = renderFlyout(false); - expect(component).toMatchSnapshot(); + expect(container).toBeEmptyDOMElement(); }); }); From eae5dff565ad61f005ef9a7cee8aeaf70ce11fcb Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 10:46:50 +0200 Subject: [PATCH 03/14] Convert call_outs tests --- .../__snapshots__/call_outs.test.tsx.snap | 41 ------------------- .../components/call_outs/call_outs.test.tsx | 24 +++++++---- .../disabled_call_out.test.tsx | 1 - 3 files changed, 16 insertions(+), 50 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/__snapshots__/call_outs.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/__snapshots__/call_outs.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/__snapshots__/call_outs.test.tsx.snap deleted file mode 100644 index 432a526ffcafc..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/__snapshots__/call_outs.test.tsx.snap +++ /dev/null @@ -1,41 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CallOuts should render normally 1`] = ` - - - - } - > -

- - - , - } - } - /> -

-
-
-`; - -exports[`CallOuts should render without any call outs 1`] = `""`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/call_outs.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/call_outs.test.tsx index 0e4658ea7f895..720737ff28ac2 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/call_outs.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/call_outs/call_outs.test.tsx @@ -8,27 +8,35 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; - import { CallOuts } from '.'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; describe('CallOuts', () => { - test('should render normally', () => { - const component = shallow( + it('should render normally', () => { + renderWithI18n( ); - expect(component).toMatchSnapshot(); + expect(screen.getByText('Deprecated languages in use')).toBeVisible(); + expect( + screen.getByText('The following deprecated languages are in use: php.', { exact: false }) + ).toBeVisible(); + expect(screen.getByRole('link', { name: 'Painless' })).toHaveAttribute( + 'href', + 'http://www.elastic.co/painlessDocs' + ); }); - test('should render without any call outs', () => { - const component = shallow( + it('should render without any call outs', () => { + const { container } = renderWithI18n( ); - expect(component).toMatchSnapshot(); + expect(container).toBeEmptyDOMElement(); + expect(screen.queryByText('Deprecated languages in use')).not.toBeInTheDocument(); }); }); diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx index b9efa929da5d9..8229a1142ff17 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/field_editor/components/scripting_call_outs/disabled_call_out.test.tsx @@ -8,7 +8,6 @@ */ import React from 'react'; - import { ScriptingDisabledCallOut } from './disabled_call_out'; import { renderWithI18n } from '@kbn/test-jest-helpers'; import { screen } from '@testing-library/react'; From 528534a0b21f04daf3779c2a1dd46f22850bf42c Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 12:31:38 +0200 Subject: [PATCH 04/14] Convert scipted_field_table test --- .../scripted_field_table.test.tsx | 269 +++++++++--------- 1 file changed, 132 insertions(+), 137 deletions(-) diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx index 5e1943aa894db..0c16b57fd14cd 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx @@ -7,214 +7,209 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { DataView } from '@kbn/data-views-plugin/public'; +import type { FieldSpec } from '@kbn/data-views-plugin/common'; import React from 'react'; -import type { ShallowWrapper } from 'enzyme'; -import { shallow } from 'enzyme'; - +import userEvent from '@testing-library/user-event'; +import { createStubDataView } from '@kbn/data-views-plugin/public/data_views/data_view.stub'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen, within } from '@testing-library/react'; import { ScriptedFieldsTable } from '.'; -import type { DataView } from '@kbn/data-views-plugin/public'; -jest.mock('@elastic/eui', () => ({ - EuiTitle: 'eui-title', - EuiText: 'eui-text', - EuiHorizontalRule: 'eui-horizontal-rule', - EuiSpacer: 'eui-spacer', - EuiCallOut: 'eui-call-out', - EuiLink: 'eui-link', - EuiOverlayMask: 'eui-overlay-mask', - EuiConfirmModal: 'eui-confirm-modal', - Comparators: { - property: () => {}, - default: () => {}, - }, -})); -jest.mock('./components/header', () => ({ Header: 'header' })); -jest.mock('./components/call_outs', () => ({ CallOuts: 'call-outs' })); -jest.mock('./components/table', () => ({ - // Note: this seems to fix React complaining about non lowercase attributes - Table: () => { - return 'table'; - }, +jest.mock('@kbn/kibana-react-plugin/public', () => ({ + useKibana: () => ({ + services: { + docLinks: { + links: { + indexPatterns: { + runtimeFields: '#', + }, + query: { + queryESQL: '#', + }, + }, + }, + }, + }), })); const helpers = { - redirectToRoute: () => {}, - getRouteHref: () => '#', + getRouteHref: jest.fn(), + redirectToRoute: jest.fn(), }; -const getIndexPatternMock = (mockedFields: any = {}) => ({ ...mockedFields } as DataView); +const createDataViewWithScriptedFields = (scriptedFields: FieldSpec[]) => + createStubDataView({ + spec: { + title: 'test-data-view', + fields: Object.fromEntries(scriptedFields.map((field) => [field.name, field])), + }, + }); + +const createScriptedField = ({ + name, + lang = 'painless', + script, +}: { + name: string; + lang?: string; + script: string; +}): FieldSpec => ({ + aggregatable: false, + lang, + name, + script, + scripted: true, + searchable: false, + type: 'number', +}); + +const getDeleteButtonForField = (fieldName: string) => + within(screen.getByRole('row', { name: new RegExp(fieldName) })).getAllByRole('button', { + name: 'Edit', + })[1]; describe('ScriptedFieldsTable', () => { let indexPattern: DataView; beforeEach(() => { - indexPattern = getIndexPatternMock({ - getScriptedFields: () => [ - { isUserEditable: true, name: 'ScriptedField', lang: 'painless', script: 'x++' }, - { - isUserEditable: false, - name: 'JustATest', - lang: 'painless', - script: 'z++', - }, - ], - }) as DataView; + indexPattern = createDataViewWithScriptedFields([ + createScriptedField({ name: 'ScriptedField', script: 'x++' }), + createScriptedField({ name: 'JustATest', script: 'z++' }), + ]); + jest.spyOn(console, 'warn').mockImplementation(() => {}); // Silent EUI warnings during tests + }); + + afterEach(() => { + jest.restoreAllMocks(); }); - test('should render normally', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< - typeof ScriptedFieldsTable - >( + it('should render normally', () => { + renderWithI18n( {}} - userEditPermission={false} scriptedFieldLanguageFilter={[]} + userEditPermission={false} /> ); - // Allow the componentWillMount code to execute - // https://github.com/airbnb/enzyme/issues/450 - await component.update(); // Fire `componentWillMount()` - await component.update(); // Force update the component post async actions - - expect(component).toMatchSnapshot(); + expect(screen.getByText('Scripted fields are deprecated')).toBeVisible(); + expect(screen.getByTestId('tableHeaderCell_displayName_0')).toBeVisible(); + expect(screen.getByText('ScriptedField')).toBeVisible(); + expect(screen.getByText('JustATest')).toBeVisible(); + expect(screen.queryByText('Deprecated languages in use')).not.toBeInTheDocument(); }); - test('should filter based on the query bar', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( + it('should filter based on the query bar', () => { + renderWithI18n( {}} - userEditPermission={false} scriptedFieldLanguageFilter={[]} + userEditPermission={false} /> ); - // Allow the componentWillMount code to execute - // https://github.com/airbnb/enzyme/issues/450 - await component.update(); // Fire `componentWillMount()` - await component.update(); // Force update the component post async actions - - component.setProps({ fieldFilter: 'Just' }); - component.update(); - - expect(component).toMatchSnapshot(); + expect(screen.queryByText('ScriptedField')).not.toBeInTheDocument(); + expect(screen.getByText('JustATest')).toBeVisible(); }); - test('should filter based on the lang filter', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< - typeof ScriptedFieldsTable - >( + it('should filter based on the lang filter', () => { + const BAD_LANG = 'somethingElse'; + const indexPatternWithDeprecatedLang = createDataViewWithScriptedFields([ + createScriptedField({ name: 'ScriptedField', script: 'x++' }), + createScriptedField({ name: 'JustATest', script: 'z++' }), + createScriptedField({ name: 'Bad', lang: BAD_LANG, script: 'z++' }), + ]); + + renderWithI18n( [ - { isUserEditable: true, name: 'ScriptedField', lang: 'painless', script: 'x++' }, - { isUserEditable: true, name: 'JustATest', lang: 'painless', script: 'z++' }, - { isUserEditable: true, name: 'Bad', lang: 'somethingElse', script: 'z++' }, - ], - }) as DataView - } - painlessDocLink={'painlessDoc'} helpers={helpers} + indexPattern={indexPatternWithDeprecatedLang} + painlessDocLink={'painlessDoc'} saveIndexPattern={async () => {}} + scriptedFieldLanguageFilter={['painless']} userEditPermission={false} - scriptedFieldLanguageFilter={[]} /> ); - // Allow the componentWillMount code to execute - // https://github.com/airbnb/enzyme/issues/450 - await component.update(); // Fire `componentWillMount()` - await component.update(); // Force update the component post async actions - - component.setProps({ scriptedFieldLanguageFilter: ['painless'] }); - component.update(); - - expect(component).toMatchSnapshot(); + expect(screen.getByText('Deprecated languages in use')).toBeVisible(); + expect( + screen.getByText(`The following deprecated languages are in use: ${BAD_LANG}.`, { + exact: false, + }) + ).toBeVisible(); + expect(screen.getByText('ScriptedField')).toBeVisible(); + expect(screen.getByText('JustATest')).toBeVisible(); + expect(screen.queryByText('Bad')).not.toBeInTheDocument(); }); - test('should hide the table if there are no scripted fields', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( + it('should show an empty table if there are no scripted fields', () => { + renderWithI18n( [], - }) as DataView - } - painlessDocLink={'painlessDoc'} helpers={helpers} + indexPattern={createDataViewWithScriptedFields([])} + painlessDocLink={'painlessDoc'} saveIndexPattern={async () => {}} - userEditPermission={false} scriptedFieldLanguageFilter={[]} + userEditPermission={false} /> ); - // Allow the componentWillMount code to execute - // https://github.com/airbnb/enzyme/issues/450 - await component.update(); // Fire `componentWillMount()` - await component.update(); // Force update the component post async actions - - expect(component).toMatchSnapshot(); + expect(screen.getByTestId('tableHeaderCell_displayName_0')).toBeVisible(); + expect(screen.getByText('No items found')).toBeVisible(); + expect(screen.queryByText('ScriptedField')).not.toBeInTheDocument(); + expect(screen.queryByText('JustATest')).not.toBeInTheDocument(); }); - test('should show a delete modal', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< - typeof ScriptedFieldsTable - >( + it('should show a delete modal', async () => { + const user = userEvent.setup(); + + renderWithI18n( {}} - userEditPermission={false} scriptedFieldLanguageFilter={[]} + userEditPermission={true} /> ); - await component.update(); // Fire `componentWillMount()` - // @ts-expect-error lang is not valid - component.instance().startDeleteField({ name: 'ScriptedField', lang: '', script: '' }); - await component.update(); + await user.click(getDeleteButtonForField('ScriptedField')); - // Ensure the modal is visible - expect(component).toMatchSnapshot(); + expect(screen.getByText("Delete scripted field 'ScriptedField'?")).toBeVisible(); }); - test('should delete a field', async () => { - const removeScriptedField = jest.fn(); - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< - typeof ScriptedFieldsTable - >( + it('should delete a field', async () => { + const user = userEvent.setup(); + + const removeScriptedFieldSpy = jest.fn(); + const saveIndexPatternSpy = jest.fn(); + jest.spyOn(indexPattern, 'removeScriptedField').mockImplementation(removeScriptedFieldSpy); + + renderWithI18n( {}} - userEditPermission={false} + saveIndexPattern={saveIndexPatternSpy} scriptedFieldLanguageFilter={[]} + userEditPermission={true} /> ); - await component.update(); // Fire `componentWillMount()` - // @ts-expect-error - component.instance().startDeleteField({ name: 'ScriptedField', lang: '', script: '' }); - - await component.update(); - // @ts-expect-error - await component.instance().deleteField(); - await component.update(); + await user.click(getDeleteButtonForField('ScriptedField')); + expect(screen.getByText("Delete scripted field 'ScriptedField'?")).toBeVisible(); + await user.click(screen.getByTestId('confirmModalConfirmButton')); - expect(removeScriptedField).toBeCalled(); + expect(removeScriptedFieldSpy).toHaveBeenCalledWith('ScriptedField'); + expect(saveIndexPatternSpy).toHaveBeenCalledWith(indexPattern); }); }); From 3fa48a74416c148fe39a7d70137a9e57306c8749 Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 12:32:54 +0200 Subject: [PATCH 05/14] Remove scipted_field_table snapshot --- .../scripted_field_table.test.tsx.snap | 185 ------------------ 1 file changed, 185 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap deleted file mode 100644 index 19172b9866085..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap +++ /dev/null @@ -1,185 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ScriptedFieldsTable should filter based on the lang filter 1`] = ` - -
- - - - -`; - -exports[`ScriptedFieldsTable should filter based on the query bar 1`] = ` - -
- - -
- -`; - -exports[`ScriptedFieldsTable should hide the table if there are no scripted fields 1`] = ` - -
- - -
- -`; - -exports[`ScriptedFieldsTable should render normally 1`] = ` - -
- - -
- -`; - -exports[`ScriptedFieldsTable should show a delete modal 1`] = ` - -
- - -
- - -`; From 7debe7586751dfaf4559b15d6d04dd6723c94e5a Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 14:51:06 +0200 Subject: [PATCH 06/14] Convert indexed_fields_table tests --- .../indexed_fields_table.test.tsx.snap | 281 ---------------- .../indexed_fields_table.test.tsx | 315 ++++++++---------- 2 files changed, 143 insertions(+), 453 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap deleted file mode 100644 index 04098ebbe9ed7..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap +++ /dev/null @@ -1,281 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should render normally 1`] = ` -Object { - "items": Array [ - Object { - "displayName": "Elastic", - "esTypes": Array [ - "keyword", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "Elastic", - "info": Array [ - "Rollup aggregations:", - "terms", - ], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "Elastic", - "searchable": true, - "type": "keyword", - }, - Object { - "displayName": "timestamp", - "esTypes": Array [ - "date", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "timestamp", - "info": Array [ - "Rollup aggregations:", - "date_histogram (interval: 30s, delay: 30s, UTC)", - ], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "timestamp", - "type": "date", - }, - Object { - "displayName": "conflictingField", - "esTypes": Array [ - "keyword", - "long", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "conflictingField", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "conflictingField", - "type": "keyword, long", - }, - Object { - "displayName": "amount", - "esTypes": Array [ - "long", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "amount", - "info": Array [ - "Rollup aggregations:", - "histogram (interval: 5)", - "avg", - "max", - "min", - "sum", - "value_count", - ], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "amount", - "type": "long", - }, - Object { - "displayName": "runtime", - "esTypes": Array [ - "long", - ], - "excluded": false, - "format": "", - "hasRuntime": true, - "id": "runtime", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": "number", - "name": "runtime", - "runtimeField": Object { - "script": Object { - "source": "emit('Hello');", - }, - "type": "long", - }, - "type": "long", - }, - ], -} -`; - -exports[`IndexedFieldsTable should filter based on the query bar 1`] = ` -Object { - "items": Array [ - Object { - "displayName": "Elastic", - "esTypes": Array [ - "keyword", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "Elastic", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "Elastic", - "searchable": true, - "type": "keyword", - }, - ], -} -`; - -exports[`IndexedFieldsTable should filter based on the schema filter 1`] = ` -Object { - "items": Array [ - Object { - "displayName": "runtime", - "esTypes": Array [ - "long", - ], - "excluded": false, - "format": "", - "hasRuntime": true, - "id": "runtime", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": "number", - "name": "runtime", - "runtimeField": Object { - "script": Object { - "source": "emit('Hello');", - }, - "type": "long", - }, - "type": "long", - }, - ], -} -`; - -exports[`IndexedFieldsTable should filter based on the type filter 1`] = ` -Object { - "items": Array [ - Object { - "displayName": "timestamp", - "esTypes": Array [ - "date", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "timestamp", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "timestamp", - "type": "date", - }, - ], -} -`; - -exports[`IndexedFieldsTable should render normally 1`] = ` -Object { - "items": Array [ - Object { - "displayName": "Elastic", - "esTypes": Array [ - "keyword", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "Elastic", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "Elastic", - "searchable": true, - "type": "keyword", - }, - Object { - "displayName": "timestamp", - "esTypes": Array [ - "date", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "timestamp", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "timestamp", - "type": "date", - }, - Object { - "displayName": "conflictingField", - "esTypes": Array [ - "keyword", - "long", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "conflictingField", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "conflictingField", - "type": "keyword, long", - }, - Object { - "displayName": "amount", - "esTypes": Array [ - "long", - ], - "excluded": false, - "format": "", - "hasRuntime": false, - "id": "amount", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": undefined, - "name": "amount", - "type": "long", - }, - Object { - "displayName": "runtime", - "esTypes": Array [ - "long", - ], - "excluded": false, - "format": "", - "hasRuntime": true, - "id": "runtime", - "info": Array [], - "isMapped": false, - "isUserEditable": false, - "kbnType": "number", - "name": "runtime", - "runtimeField": Object { - "script": Object { - "source": "emit('Hello');", - }, - "type": "long", - }, - "type": "long", - }, - ], -} -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx index 3079c84c21410..f5fc1e2351eca 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx @@ -7,69 +7,15 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { RuntimeField } from '@kbn/data-views-plugin/common'; import React from 'react'; -import type { ShallowWrapper } from 'enzyme'; -import { shallow } from 'enzyme'; import { coreMock } from '@kbn/core/public/mocks'; -import type { DataView } from '@kbn/data-views-plugin/public'; +import { createStubDataView } from '@kbn/data-views-plugin/public/data_views/data_view.stub'; import { DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; -import { IndexedFieldsTable } from './indexed_fields_table'; import { getFieldInfo } from '../../utils'; -import type { RuntimeField } from '@kbn/data-views-plugin/common'; - -jest.mock('@elastic/eui', () => ({ - EuiFlexGroup: 'eui-flex-group', - EuiFlexItem: 'eui-flex-item', - EuiIcon: 'eui-icon', - EuiInMemoryTable: 'eui-in-memory-table', -})); - -jest.mock('./components/table', () => ({ - // Note: this seems to fix React complaining about non lowercase attributes - Table: () => { - return 'table'; - }, -})); - -const helpers = { - editField: (_fieldName: string) => {}, - deleteField: (_fieldName: string[]) => {}, - // getFieldInfo handles non rollups as well - getFieldInfo, -}; - -const indexPattern = { - getNonScriptedFields: () => fields, - getFormatterForFieldNoDefault: () => ({ params: () => ({}) }), -} as unknown as DataView; - -const rollupIndexPattern = { - type: DataViewType.ROLLUP, - typeMeta: { - params: { - 'rollup-index': 'rollup', - }, - aggs: { - date_histogram: { - timestamp: { - agg: 'date_histogram', - fixed_interval: '30s', - delay: '30s', - time_zone: 'UTC', - }, - }, - terms: { Elastic: { agg: 'terms' } }, - histogram: { amount: { agg: 'histogram', interval: 5 } }, - avg: { amount: { agg: 'avg' } }, - max: { amount: { agg: 'max' } }, - min: { amount: { agg: 'min' } }, - sum: { amount: { agg: 'sum' } }, - value_count: { amount: { agg: 'value_count' } }, - }, - }, - getNonScriptedFields: () => fields, - getFormatterForFieldNoDefault: () => ({ params: () => ({}) }), -} as unknown as DataView; +import { IndexedFieldsTable } from './indexed_fields_table'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; const mockFieldToIndexPatternField = ( spec: Record @@ -78,22 +24,37 @@ const mockFieldToIndexPatternField = ( }; const runtimeField: RuntimeField = { type: 'long', script: { source: "emit('Hello');" } }; + const fields = [ { name: 'Elastic', displayName: 'Elastic', searchable: true, esTypes: ['keyword'], + type: 'string', + isUserEditable: true, + }, + { + name: 'timestamp', + displayName: 'timestamp', + esTypes: ['date'], + type: 'date', isUserEditable: true, }, - { name: 'timestamp', displayName: 'timestamp', esTypes: ['date'], isUserEditable: true }, { name: 'conflictingField', displayName: 'conflictingField', esTypes: ['keyword', 'long'], + type: 'conflict', + isUserEditable: true, + }, + { + name: 'amount', + displayName: 'amount', + esTypes: ['long'], + type: 'number', isUserEditable: true, }, - { name: 'amount', displayName: 'amount', esTypes: ['long'], isUserEditable: true }, { name: 'runtime', displayName: 'runtime', @@ -103,137 +64,147 @@ const fields = [ }, ].map(mockFieldToIndexPatternField); -const startServices = coreMock.createStart(); +const fieldsMap = Object.fromEntries(fields.map((field) => [field.name, field.spec])); + +const helpers = { + editField: (_fieldName: string) => {}, + deleteField: (_fieldName: string[]) => {}, + // getFieldInfo handles non rollups as well + getFieldInfo, +}; + +const indexPattern = createStubDataView({ + spec: { + title: 'test-data-view', + fields: fieldsMap, + }, +}); const mockedServices = { - userEditPermission: false, openModal: () => ({ onClose: new Promise(() => {}), close: async () => {} }), - theme: {} as any, + theme: {}, + userEditPermission: false, +}; + +const rollupIndexPattern = createStubDataView({ + spec: { + title: 'rollup-data-view', + fields: fieldsMap, + type: DataViewType.ROLLUP, + typeMeta: { + params: { + rollup_index: 'rollup', + }, + aggs: { + date_histogram: { + timestamp: { + agg: 'date_histogram', + fixed_interval: '30s', + delay: '30s', + time_zone: 'UTC', + }, + }, + terms: { Elastic: { agg: 'terms' } }, + histogram: { amount: { agg: 'histogram', interval: 5 } }, + avg: { amount: { agg: 'avg' } }, + max: { amount: { agg: 'max' } }, + min: { amount: { agg: 'min' } }, + sum: { amount: { agg: 'sum' } }, + value_count: { amount: { agg: 'value_count' } }, + }, + }, + }, +}); + +const startServices = coreMock.createStart(); + +// EUI field icons load asynchronously, so each test waits for a visible icon before +// asserting the table contents. +const expectFieldTypeIconVisible = async (label: string) => { + expect((await screen.findAllByTitle(label))[0]).toBeVisible(); +}; + +const expectHiddenFields = (fieldNames: string[]) => { + fieldNames.forEach((fieldName) => { + expect(screen.queryByText(fieldName)).not.toBeInTheDocument(); + }); +}; + +const expectVisibleFields = (fieldNames: string[]) => { + fieldNames.forEach((fieldName) => { + expect(screen.getByText(fieldName)).toBeVisible(); + }); +}; + +const renderIndexedFieldsTable = ( + props: Partial> +) => { + renderWithI18n( + { + return () => false; + }} + helpers={helpers} + indexedFieldTypeFilter={[]} + indexPattern={indexPattern} + schemaFieldTypeFilter={[]} + startServices={startServices} + {...mockedServices} + {...props} + /> + ); }; describe('IndexedFieldsTable', () => { + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); // Silent EUI warnings during tests + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + test('should render normally', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( - { - return () => false; - }} - indexedFieldTypeFilter={[]} - schemaFieldTypeFilter={[]} - fieldFilter="" - compositeRuntimeFields={{}} - startServices={startServices} - {...mockedServices} - /> - ); - - await new Promise((resolve) => process.nextTick(resolve)); - component.update(); - - expect({ items: component.props().children.props.items }).toMatchSnapshot(); + renderIndexedFieldsTable({}); + + await expectFieldTypeIconVisible('Keyword'); + expect(screen.getByTestId('tableHeaderCell_displayName_0')).toBeVisible(); + expectVisibleFields(['Elastic', 'timestamp', 'conflictingField', 'amount', 'runtime']); }); test('should filter based on the query bar', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( - { - return () => false; - }} - indexedFieldTypeFilter={[]} - schemaFieldTypeFilter={[]} - fieldFilter="" - compositeRuntimeFields={{}} - startServices={startServices} - {...mockedServices} - /> - ); - - await new Promise((resolve) => process.nextTick(resolve)); - component.setProps({ fieldFilter: 'Elast' }); - component.update(); - - expect({ items: component.props().children.props.items }).toMatchSnapshot(); + renderIndexedFieldsTable({ fieldFilter: 'Elast' }); + + await expectFieldTypeIconVisible('Keyword'); + expectVisibleFields(['Elastic']); + expectHiddenFields(['timestamp', 'conflictingField', 'amount', 'runtime']); }); test('should filter based on the type filter', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( - { - return () => false; - }} - indexedFieldTypeFilter={[]} - schemaFieldTypeFilter={[]} - fieldFilter="" - compositeRuntimeFields={{}} - startServices={startServices} - {...mockedServices} - /> - ); - - await new Promise((resolve) => process.nextTick(resolve)); - component.setProps({ indexedFieldTypeFilter: ['date'] }); - component.update(); - - expect({ items: component.props().children.props.items }).toMatchSnapshot(); + renderIndexedFieldsTable({ indexedFieldTypeFilter: ['date'] }); + + await expectFieldTypeIconVisible('Date'); + expectVisibleFields(['timestamp']); + expectHiddenFields(['Elastic', 'conflictingField', 'amount', 'runtime']); }); test('should filter based on the schema filter', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( - { - return () => false; - }} - indexedFieldTypeFilter={[]} - schemaFieldTypeFilter={[]} - fieldFilter="" - compositeRuntimeFields={{}} - startServices={startServices} - {...mockedServices} - /> - ); - - await new Promise((resolve) => process.nextTick(resolve)); - component.setProps({ schemaFieldTypeFilter: ['runtime'] }); - component.update(); - - expect({ items: component.props().children.props.items }).toMatchSnapshot(); + renderIndexedFieldsTable({ schemaFieldTypeFilter: ['runtime'] }); + + await expectFieldTypeIconVisible('Number'); + expectVisibleFields(['runtime']); + expectHiddenFields(['Elastic', 'timestamp', 'conflictingField', 'amount']); }); describe('IndexedFieldsTable with rollup index pattern', () => { test('should render normally', async () => { - const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( - { - return () => false; - }} - indexedFieldTypeFilter={[]} - schemaFieldTypeFilter={[]} - fieldFilter="" - compositeRuntimeFields={{}} - startServices={startServices} - {...mockedServices} - /> - ); - - await new Promise((resolve) => process.nextTick(resolve)); - component.update(); - - expect({ items: component.props().children.props.items }).toMatchSnapshot(); + renderIndexedFieldsTable({ indexPattern: rollupIndexPattern }); + + await expectFieldTypeIconVisible('Keyword'); + expectVisibleFields(['Elastic', 'timestamp', 'conflictingField', 'amount', 'runtime']); }); }); }); From 62ec44ed908c869764f23c46b534e3522a0b2e74 Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 16:51:12 +0200 Subject: [PATCH 07/14] Convert table tests --- .../table/__snapshots__/table.test.tsx.snap | 364 ----------------- .../components/table/table.test.tsx | 380 ++++++++++-------- 2 files changed, 209 insertions(+), 535 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap deleted file mode 100644 index 960b367fe4b6b..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap +++ /dev/null @@ -1,364 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Table render conflict summary modal 1`] = ` - - - - - - - - -

- - message - , - } - } - /> -

- - } - rowHeader="firstName" - tableCaption="Field type conflicts across indices" - tableLayout="auto" - /> -
-
- - - - - -
-`; - -exports[`Table render name 1`] = ` - - - - customer - - -`; - -exports[`Table render name 2`] = ` - - - - customer - - - -`; - -exports[`Table should render conflicting type 1`] = ` - - text, long - - - - Conflict - - - -`; - -exports[`Table should render mixed, non-conflicting type 1`] = ` - - keyword, constant_keyword - -`; - -exports[`Table should render normal field name 1`] = ` - - - - Elastic - - -`; - -exports[`Table should render normal type 1`] = ` - - string - -`; - -exports[`Table should render normally 1`] = ` - -`; - -exports[`Table should render the boolean template (false) 1`] = ``; - -exports[`Table should render the boolean template (true) 1`] = ` - - Is searchable - -`; - -exports[`Table should render timestamp field name 1`] = ` - - - - timestamp - - - -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index aa4482a85e695..1225f7c350ba7 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -7,248 +7,286 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; -import { shallow } from 'enzyme'; -import type { DataView } from '@kbn/data-views-plugin/public'; import type { IndexedFieldItem } from '../../types'; +import React from 'react'; +import userEvent from '@testing-library/user-event'; +import { coreMock, overlayServiceMock } from '@kbn/core/public/mocks'; +import { createStubDataView } from '@kbn/data-views-plugin/public/data_views/data_view.stub'; import { - TableWithoutPersist as Table, - renderFieldName, getConflictModalContent, + renderFieldName, showDelete, + TableWithoutPersist as Table, } from './table'; -import { coreMock, overlayServiceMock } from '@kbn/core/public/mocks'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; const coreStart = coreMock.createStart(); -const indexPattern = { - timeFieldName: 'timestamp', -} as DataView; +const indexPattern = createStubDataView({ + spec: { + timeFieldName: 'timestamp', + title: 'test-data-view', + }, +}); + +const createIndexedField = ({ + name, + ...field +}: Pick & Partial): IndexedFieldItem => ({ + displayName: name, + excluded: false, + hasRuntime: false, + info: [], + isMapped: true, + isUserEditable: true, + kbnType: 'string', + name, + type: 'keyword', + ...field, +}); -const items: IndexedFieldItem[] = [ - { +const items = [ + createIndexedField({ name: 'Elastic', - displayName: 'Elastic', searchable: true, - info: [], type: 'name', - kbnType: 'string', - excluded: false, - isMapped: true, - isUserEditable: true, - hasRuntime: false, - }, - { + }), + createIndexedField({ + kbnType: 'date', name: 'timestamp', - displayName: 'timestamp', type: 'date', - kbnType: 'date', - info: [], - excluded: false, - isMapped: true, - isUserEditable: true, - hasRuntime: false, - }, - { - name: 'conflictingField', - displayName: 'conflictingField', + }), + createIndexedField({ conflictDescriptions: { keyword: ['index_a'], long: ['index_b'] }, - type: 'text, long', kbnType: 'conflict', - info: [], - excluded: false, - isMapped: true, - isUserEditable: true, - hasRuntime: false, - }, - { + name: 'conflictingField', + type: 'text, long', + }), + createIndexedField({ + hasRuntime: true, + isMapped: false, + kbnType: 'text', name: 'customer', - displayName: 'customer', type: 'keyword', - kbnType: 'text', - info: [], - excluded: false, - isMapped: false, - isUserEditable: true, + }), + createIndexedField({ hasRuntime: true, - }, - { - name: 'noedit', - displayName: 'noedit', - type: 'keyword', - kbnType: 'text', - info: [], - excluded: false, isMapped: false, isUserEditable: false, - hasRuntime: true, - }, + kbnType: 'text', + name: 'noedit', + type: 'keyword', + }), ]; -const baseProps = { +const mixedTypeItem = createIndexedField({ + name: 'mixedType', + type: 'keyword, constant_keyword', +}); + +const primitiveRuntimeField = createIndexedField({ + hasRuntime: true, + isMapped: false, + name: 'runtimePrimitive', + runtimeField: { + type: 'keyword', + }, +}); + +const compositeRuntimeField = createIndexedField({ + hasRuntime: true, + isMapped: false, + name: 'runtimeComposite', + runtimeField: { + type: 'composite', + }, +}); + +const compositeRuntimeDefinition = createIndexedField({ + hasRuntime: true, + isMapped: false, + name: 'runtimeCompositeDefinition', + runtimeField: { + type: 'composite', + }, + type: 'composite', +}); + +const baseProps: Pick, 'euiTablePersist'> = { euiTablePersist: { + onTableChange: jest.fn(), pageSize: 10, - onTableChange: () => {}, - sorting: { sort: { direction: 'asc' as const, field: 'name' as const } }, + sorting: { sort: { direction: 'asc', field: 'name' } }, }, }; -const renderTable = ( - { editField } = { - editField: () => {}, - } -) => - shallow( +const renderTable = ({ + deleteField = jest.fn(), + editField = jest.fn(), + tableItems = items, +}: { + deleteField?: React.ComponentProps['deleteField']; + editField?: React.ComponentProps['editField']; + tableItems?: IndexedFieldItem[]; +} = {}) => + renderWithI18n(
{}} + indexPattern={indexPattern} + items={tableItems} openModal={overlayServiceMock.createStartContract().openModal} startServices={coreStart} /> ); +const getRowByText = (text: string) => { + const row = screen.getByText(text).closest('tr'); + + if (!row) throw new Error(`Unable to find row for ${text}`); + + return row; +}; + describe('Table', () => { - test('should render normally', () => { - expect(renderTable()).toMatchSnapshot(); + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); + }); + + afterEach(() => { + jest.restoreAllMocks(); }); - test('should render normal field name', () => { - const tableCell = shallow(renderTable().prop('columns')[0].render('Elastic', items[0])); - expect(tableCell).toMatchSnapshot(); + it('should render normally', async () => { + renderTable(); + + expect(await screen.findByText('Elastic')).toBeVisible(); + expect(screen.getByText('timestamp')).toBeVisible(); + expect(screen.getByText('conflictingField')).toBeVisible(); + expect(screen.getByText('customer')).toBeVisible(); + expect(screen.getByText('noedit')).toBeVisible(); }); - test('should render timestamp field name', () => { - const tableCell = shallow(renderTable().prop('columns')[0].render('timestamp', items[1])); - expect(tableCell).toMatchSnapshot(); + it('should render normal field name', async () => { + renderWithI18n(renderFieldName(items[0])); + + expect(await screen.findByText('String')).toBeVisible(); + expect(screen.getByTestId('field-name-Elastic')).toHaveTextContent('Elastic'); }); - test('should render the boolean template (true)', () => { - const tableCell = shallow(renderTable().prop('columns')[3].render(true)); - expect(tableCell).toMatchSnapshot(); + it('should render timestamp field name', async () => { + renderWithI18n(renderFieldName(items[1], indexPattern.timeFieldName)); + + expect(await screen.findByText('Date')).toBeVisible(); + expect(screen.getByTestId('field-name-timestamp')).toHaveTextContent('timestamp'); + expect(screen.getByText('Primary time field')).toBeVisible(); }); - test('should render the boolean template (false)', () => { - const tableCell = shallow(renderTable().prop('columns')[3].render(false, items[2])); - expect(tableCell).toMatchSnapshot(); + it('should render the boolean template (true)', async () => { + renderTable(); + + expect(await screen.findByText('Is searchable')).toBeVisible(); }); - test('should render normal type', () => { - const tableCell = shallow(renderTable().prop('columns')[1].render('string', {})); - expect(tableCell).toMatchSnapshot(); + it('should render the boolean template (false)', async () => { + renderTable(); + + expect(getRowByText('conflictingField')).not.toHaveTextContent('Is searchable'); }); - test('should render conflicting type', () => { - const tableCell = shallow( - renderTable() - .prop('columns')[1] - .render('text, long', { - kbnType: 'conflict', - conflictDescriptions: { keyword: ['index_a'], long: ['index_b'] }, - }) - ); - expect(tableCell).toMatchSnapshot(); + it('should render normal type', async () => { + renderTable(); + + expect((await screen.findByText('Elastic')).closest('tr')).toHaveTextContent('name'); }); - test('should render mixed, non-conflicting type', () => { - const tableCell = shallow( - renderTable().prop('columns')[1].render('keyword, constant_keyword', { - kbnType: 'string', - }) - ); - expect(tableCell).toMatchSnapshot(); + it('should render conflicting type', async () => { + renderTable(); + + const conflictingFieldRow = (await screen.findByText('conflictingField')).closest('tr'); + expect(conflictingFieldRow).toHaveTextContent('text, long'); + expect(conflictingFieldRow).toHaveTextContent('Conflict'); }); - test('should allow edits', () => { + it('should render mixed, non-conflicting type', async () => { + renderTable({ tableItems: [mixedTypeItem] }); + + const mixedTypeRow = (await screen.findByText('mixedType')).closest('tr'); + expect(mixedTypeRow).toHaveTextContent('keyword, constant_keyword'); + expect(mixedTypeRow).not.toHaveTextContent('Conflict'); + }); + + it('should allow edits', async () => { + const user = userEvent.setup(); const editField = jest.fn(); - // Click the edit button - renderTable({ editField }).prop('columns')[6].actions[0].onClick(); - expect(editField).toBeCalled(); + renderTable({ editField }); + + await user.click(screen.getAllByTestId('editFieldFormat')[0]); + + expect(editField).toHaveBeenCalled(); }); - test('should not allow edit or deletion for user with only read access', () => { - const editAvailable = renderTable().prop('columns')[6].actions[0].available(items[4]); - const deleteAvailable = renderTable().prop('columns')[6].actions[1].available(items[4]); - expect(editAvailable).toBeFalsy(); - expect(deleteAvailable).toBeFalsy(); + it('should not allow edit or deletion for user with only read access', async () => { + renderTable({ tableItems: [items[4]] }); + + const noEditRow = (await screen.findByText('noedit')).closest('tr'); + expect(noEditRow).toBeVisible(); + expect(screen.queryByTestId('editFieldFormat')).not.toBeInTheDocument(); + expect(screen.queryByTestId('deleteField')).not.toBeInTheDocument(); }); - test('render name', () => { - const mappedField = { - name: 'customer', - info: [], - excluded: false, - kbnType: 'string', - type: 'keyword', - isMapped: true, - isUserEditable: true, - hasRuntime: false, - }; - - expect(renderFieldName(mappedField)).toMatchSnapshot(); - - const runtimeField = { - name: 'customer', - info: [], - excluded: false, - kbnType: 'string', - type: 'keyword', - isMapped: false, - isUserEditable: true, + it('render name', async () => { + const mappedField = createIndexedField({ name: 'customer' }); + + const { unmount } = renderWithI18n(renderFieldName(mappedField)); + + expect(await screen.findByText('String')).toBeVisible(); + expect(screen.getByTestId('field-name-customer')).toHaveTextContent('customer'); + + unmount(); + + const runtimeField = createIndexedField({ hasRuntime: true, - }; + isMapped: false, + name: 'customer', + }); + + renderWithI18n(renderFieldName(runtimeField)); - expect(renderFieldName(runtimeField)).toMatchSnapshot(); + expect(await screen.findByText('String')).toBeVisible(); + expect(screen.getByTestId('field-name-customer')).toHaveTextContent('customer'); + expect(screen.getByText('Info')).toBeVisible(); }); - test('render conflict summary modal ', () => { - expect( + it('render conflict summary modal ', () => { + renderWithI18n( getConflictModalContent({ closeFn: () => {}, - fieldName: 'message', conflictDescriptions: { keyword: ['index_a'], long: ['index_b'] }, + fieldName: 'message', }) - ).toMatchSnapshot(); - }); + ); - test('showDelete', () => { - const runtimeFields = [ - { - name: 'customer', - info: [], - excluded: false, - kbnType: 'string', - type: 'keyword', - isMapped: false, - isUserEditable: true, - hasRuntime: true, - runtimeField: { - type: 'keyword', - }, - }, - { - name: 'thing', - info: [], - excluded: false, - kbnType: 'string', - type: 'keyword', - isMapped: false, - isUserEditable: true, - hasRuntime: true, - runtimeField: { - type: 'composite', - }, - }, - ] as IndexedFieldItem[]; + expect(screen.getByText('This field has a type conflict')).toBeVisible(); + expect(screen.getByText(/The type of the/)).toHaveTextContent('message'); + expect(screen.getByText('keyword')).toBeVisible(); + expect(screen.getByText('index_a')).toBeVisible(); + expect(screen.getByText('long')).toBeVisible(); + expect(screen.getByText('index_b')).toBeVisible(); + expect(screen.getByText('Close')).toBeVisible(); + }); + it('showDelete', () => { // indexed field expect(showDelete(items[0])).toBe(false); // runtime field - primitive type - expect(showDelete(runtimeFields[0])).toBe(true); - // runtime field - composite type - expect(showDelete(runtimeFields[1])).toBe(false); + expect(showDelete(primitiveRuntimeField)).toBe(true); + // runtime field - composite subfield + expect(showDelete(compositeRuntimeField)).toBe(false); + // runtime field - composite definition + expect(showDelete(compositeRuntimeDefinition)).toBe(true); }); }); From 2772f281a4b0d6a482e25244c130f83daa75cecb Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 17:08:39 +0200 Subject: [PATCH 08/14] Unify the test naming convention --- .../components/table/table.test.tsx | 4 +++- .../indexed_fields_table.test.tsx | 10 +++++----- .../confirmation_modal.test.tsx | 2 +- .../components/header/header.test.tsx | 2 +- .../components/table/table.test.tsx | 18 +++++++++--------- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index 1225f7c350ba7..dae53a0ab6950 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -193,7 +193,9 @@ describe('Table', () => { it('should render the boolean template (false)', async () => { renderTable(); - expect(getRowByText('conflictingField')).not.toHaveTextContent('Is searchable'); + expect((await screen.findByText('conflictingField')).closest('tr')).not.toHaveTextContent( + 'Is searchable' + ); }); it('should render normal type', async () => { diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx index f5fc1e2351eca..54eedada072cf 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx @@ -167,7 +167,7 @@ describe('IndexedFieldsTable', () => { jest.restoreAllMocks(); }); - test('should render normally', async () => { + it('should render normally', async () => { renderIndexedFieldsTable({}); await expectFieldTypeIconVisible('Keyword'); @@ -175,7 +175,7 @@ describe('IndexedFieldsTable', () => { expectVisibleFields(['Elastic', 'timestamp', 'conflictingField', 'amount', 'runtime']); }); - test('should filter based on the query bar', async () => { + it('should filter based on the query bar', async () => { renderIndexedFieldsTable({ fieldFilter: 'Elast' }); await expectFieldTypeIconVisible('Keyword'); @@ -183,7 +183,7 @@ describe('IndexedFieldsTable', () => { expectHiddenFields(['timestamp', 'conflictingField', 'amount', 'runtime']); }); - test('should filter based on the type filter', async () => { + it('should filter based on the type filter', async () => { renderIndexedFieldsTable({ indexedFieldTypeFilter: ['date'] }); await expectFieldTypeIconVisible('Date'); @@ -191,7 +191,7 @@ describe('IndexedFieldsTable', () => { expectHiddenFields(['Elastic', 'conflictingField', 'amount', 'runtime']); }); - test('should filter based on the schema filter', async () => { + it('should filter based on the schema filter', async () => { renderIndexedFieldsTable({ schemaFieldTypeFilter: ['runtime'] }); await expectFieldTypeIconVisible('Number'); @@ -200,7 +200,7 @@ describe('IndexedFieldsTable', () => { }); describe('IndexedFieldsTable with rollup index pattern', () => { - test('should render normally', async () => { + it('should render normally', async () => { renderIndexedFieldsTable({ indexPattern: rollupIndexPattern }); await expectFieldTypeIconVisible('Keyword'); diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx index a32281307ef5c..0d25db80d1f0e 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/confirmation_modal/confirmation_modal.test.tsx @@ -13,7 +13,7 @@ import { renderWithI18n } from '@kbn/test-jest-helpers'; import { screen } from '@testing-library/react'; describe('Header', () => { - test('should render normally', () => { + it('should render normally', () => { renderWithI18n( { - test('should render normally', () => { + it('should render normally', () => { renderWithI18n(
); expect( diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx index 89a65123c8d3d..32571d45115c3 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx @@ -57,7 +57,7 @@ describe('Table', () => { jest.clearAllMocks(); }); - test('renders table headers and rows', () => { + it('renders table headers and rows', () => { renderTable({ isSaving: true }); expect(screen.getByRole('table')).toBeInTheDocument(); @@ -66,7 +66,7 @@ describe('Table', () => { expect(screen.getByText('tim*')).toBeInTheDocument(); }); - test('renders filter matches when found', () => { + it('renders filter matches when found', () => { renderTable({ indexPattern: getIndexPatternMock(['time', 'value']), fieldWildcardMatcher: () => () => true, @@ -75,7 +75,7 @@ describe('Table', () => { expect(screen.getByText('time, value')).toBeInTheDocument(); }); - test('renders no matches message when there are no matches', () => { + it('renders no matches message when there are no matches', () => { renderTable({ indexPattern: getIndexPatternMock(['value']), }); @@ -86,7 +86,7 @@ describe('Table', () => { }); describe('editing', () => { - test('shows input and save button after entering edit mode', async () => { + it('shows input and save button after entering edit mode', async () => { const user = userEvent.setup(); renderTable(); @@ -96,7 +96,7 @@ describe('Table', () => { expect(screen.getByTestId('save_filter-tim*')).toBeInTheDocument(); }); - test('updates matches dynamically as input value changes', async () => { + it('updates matches dynamically as input value changes', async () => { const user = userEvent.setup(); renderTable({ indexPattern: getIndexPatternMock(['time', 'value']), @@ -110,7 +110,7 @@ describe('Table', () => { expect(screen.queryByText('time, value')).not.toBeInTheDocument(); }); - test('saves edited filter when save icon is clicked and exits edit mode', async () => { + it('saves edited filter when save icon is clicked and exits edit mode', async () => { const user = userEvent.setup(); const saveFilter = jest.fn(); renderTable({ saveFilter }); @@ -125,7 +125,7 @@ describe('Table', () => { }); }); - test('allows deletes', async () => { + it('allows deletes', async () => { const user = userEvent.setup(); const deleteFilter = jest.fn(); renderTable({ deleteFilter }); @@ -135,7 +135,7 @@ describe('Table', () => { expect(deleteFilter).toHaveBeenCalledWith({ clientId: '1', value: 'tim*' }); }); - test('saves when in edit mode and Enter key is pressed', async () => { + it('saves when in edit mode and Enter key is pressed', async () => { const user = userEvent.setup(); const saveFilter = jest.fn(); renderTable({ saveFilter }); @@ -150,7 +150,7 @@ describe('Table', () => { expect(screen.queryByRole('textbox')).not.toBeInTheDocument(); }); - test('cancels when in edit mode and Escape key is pressed', async () => { + it('cancels when in edit mode and Escape key is pressed', async () => { const user = userEvent.setup(); const saveFilter = jest.fn(); renderTable({ saveFilter }); From e2a6198e74e45e135f6cece160a04d51f73dae2c Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 17:39:27 +0200 Subject: [PATCH 09/14] Convert source_filters_table tests --- .../components/table/table.test.tsx | 8 - .../source_filters_table.test.tsx.snap | 280 ----------------- .../source_filters_table.test.tsx | 287 +++++++++--------- 3 files changed, 147 insertions(+), 428 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/__snapshots__/source_filters_table.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index dae53a0ab6950..38f7f053ed2f7 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -142,14 +142,6 @@ const renderTable = ({ /> ); -const getRowByText = (text: string) => { - const row = screen.getByText(text).closest('tr'); - - if (!row) throw new Error(`Unable to find row for ${text}`); - - return row; -}; - describe('Table', () => { beforeEach(() => { jest.spyOn(console, 'warn').mockImplementation(() => {}); diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/__snapshots__/source_filters_table.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/__snapshots__/source_filters_table.test.tsx.snap deleted file mode 100644 index 6a2b208c47987..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/__snapshots__/source_filters_table.test.tsx.snap +++ /dev/null @@ -1,280 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SourceFiltersTable should add a filter 1`] = ` - -
- - -
- -`; - -exports[`SourceFiltersTable should filter based on the query bar 1`] = ` - -
- - -
- -`; - -exports[`SourceFiltersTable should remove a filter 1`] = ` - -
- - -
- -`; - -exports[`SourceFiltersTable should render normally 1`] = ` - -
- - -
- -`; - -exports[`SourceFiltersTable should should a loading indicator when saving 1`] = ` - -
- - -
- -`; - -exports[`SourceFiltersTable should show a delete modal 1`] = ` - -
- - -
- - -`; - -exports[`SourceFiltersTable should update a filter 1`] = ` - -
- - -
- -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx index 4117d58051817..56adc277eb913 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx @@ -8,168 +8,175 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; - +import { createStubDataView } from '@kbn/data-views-plugin/public/data_views/data_view.stub'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen, waitFor } from '@testing-library/react'; import { SourceFiltersTable } from './source_filters_table'; -import type { DataView } from '@kbn/data-views-plugin/public'; - -jest.mock('@elastic/eui', () => ({ - EuiButton: 'eui-button', - EuiTitle: 'eui-title', - EuiText: 'eui-text', - EuiHorizontalRule: 'eui-horizontal-rule', - EuiSpacer: 'eui-spacer', - EuiCallOut: 'eui-call-out', - EuiLink: 'eui-link', - EuiOverlayMask: 'eui-overlay-mask', - EuiConfirmModal: 'eui-confirm-modal', - EuiLoadingSpinner: 'eui-loading-spinner', - Comparators: { - property: () => {}, - default: () => {}, - }, -})); - -jest.mock('./components/header', () => ({ Header: 'header' })); -jest.mock('./components/table', () => ({ - // Note: this seems to fix React complaining about non lowercase attributes - Table: () => { - return 'table'; - }, -})); - -const getIndexPatternMock = (mockedFields: any = {}) => - ({ - sourceFilters: [{ value: 'time*' }, { value: 'nam*' }, { value: 'age*' }], - ...mockedFields, - } as DataView); +import { userEvent } from '@testing-library/user-event'; + +const createDataView = ( + sourceFilters = [{ value: 'time*' }, { value: 'nam*' }, { value: 'age*' }] +) => + createStubDataView({ + spec: { + fields: { + time: { name: 'time', type: 'date', searchable: true, aggregatable: true }, + name: { name: 'name', type: 'string', searchable: true, aggregatable: true }, + age: { name: 'age', type: 'number', searchable: true, aggregatable: true }, + }, + sourceFilters, + title: 'test-data-view', + }, + }); + +const fieldWildcardMatcher = (filters: string[]) => { + const [query = ''] = filters; + const normalizedQuery = query.replace('*', ''); + + return (field: string) => field.includes(normalizedQuery); +}; + +const getFilterActionButton = (filterValue: string, buttonIndex: number) => { + const button = getFilterRow(filterValue).querySelectorAll('button')[buttonIndex]; + + if (!button) { + throw new Error(`Unable to find action button ${buttonIndex} for filter ${filterValue}`); + } + + return button; +}; + +const getFilterRow = (filterValue: string) => { + const row = screen.getByText(filterValue).closest('tr'); + + if (!row) throw new Error(`Unable to find row for filter ${filterValue}`); + + return row; +}; + +const renderSourceFiltersTable = ({ + filterFilter = '', + indexPattern = createDataView(), + saveIndexPattern = jest.fn(async () => {}), +}: Partial> = {}) => { + renderWithI18n( + + ); + + return { indexPattern, saveIndexPattern }; +}; describe('SourceFiltersTable', () => { - test('should render normally', () => { - const component = shallow( - {}} - filterFilter={''} - saveIndexPattern={async () => {}} - /> - ); - - expect(component).toMatchSnapshot(); + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); }); - test('should filter based on the query bar', () => { - const component = shallow( - {}} - filterFilter={''} - saveIndexPattern={async () => {}} - /> - ); - - component.setProps({ filterFilter: 'ti' }); - expect(component).toMatchSnapshot(); + afterEach(() => { + jest.restoreAllMocks(); }); - test('should should a loading indicator when saving', () => { - const component = shallow( - {}} - saveIndexPattern={async () => {}} - /> - ); - - component.setState({ isSaving: true }); - expect(component).toMatchSnapshot(); + it('should render normally', () => { + renderSourceFiltersTable(); + + expect(screen.getByText('time*')).toBeVisible(); + expect(screen.getByText('nam*')).toBeVisible(); + expect(screen.getByText('age*')).toBeVisible(); + expect(screen.getByTestId('fieldFilterInput')).toBeVisible(); + expect(screen.getByText('Add')).toBeVisible(); }); - test('should show a delete modal', () => { - const component = shallow( - {}} - saveIndexPattern={async () => {}} - /> - ); - - component.instance().startDeleteFilter({ value: 'tim*', clientId: 1 }); - component.update(); // We are not calling `.setState` directly so we need to re-render - expect(component).toMatchSnapshot(); + it('should filter based on the query bar', () => { + renderSourceFiltersTable({ filterFilter: 'ti' }); + + expect(screen.getByText('time*')).toBeVisible(); + expect(screen.queryByText('nam*')).not.toBeInTheDocument(); + expect(screen.queryByText('age*')).not.toBeInTheDocument(); }); - test('should remove a filter', async () => { + it('should show a loading indicator when saving', async () => { + const user = userEvent.setup(); const saveIndexPattern = jest.fn(async () => {}); - const component = shallow( - {}} - saveIndexPattern={saveIndexPattern} - /> - ); - - component.instance().startDeleteFilter({ value: 'tim*', clientId: 1 }); - component.update(); // We are not calling `.setState` directly so we need to re-render - await component.instance().deleteFilter(); - component.update(); // We are not calling `.setState` directly so we need to re-render - expect(saveIndexPattern).toBeCalled(); - expect(component).toMatchSnapshot(); + renderSourceFiltersTable({ + indexPattern: createDataView([{ value: 'tim*' }]), + saveIndexPattern, + }); + + await user.type(screen.getByTestId('fieldFilterInput'), 'na*'); + await user.click(screen.getByText('Add')); + + expect(saveIndexPattern).toHaveBeenCalled(); + expect(screen.getByText('tim*')).toBeVisible(); + }); + + it('should show a delete modal', async () => { + const user = userEvent.setup(); + renderSourceFiltersTable({ indexPattern: createDataView([{ value: 'tim*' }]) }); + + await user.click(getFilterActionButton('tim*', 1)); + + expect(screen.getByText("Delete field filter 'tim*'?")).toBeVisible(); + }); + + it('should remove a filter', async () => { + const user = userEvent.setup(); + const saveIndexPattern = jest.fn(async () => {}); + + const { indexPattern } = renderSourceFiltersTable({ + indexPattern: createDataView([{ value: 'tim*' }, { value: 'na*' }]), + saveIndexPattern, + }); + await user.click(getFilterActionButton('tim*', 1)); + await user.click(screen.getByTestId('confirmModalConfirmButton')); + + expect(saveIndexPattern).toHaveBeenCalled(); + await waitFor(() => { + expect(screen.queryByText('tim*')).not.toBeInTheDocument(); + }); + + expect(screen.getByText('na*')).toBeVisible(); + expect(indexPattern.sourceFilters).toEqual([{ value: 'na*', clientId: 2 }]); }); - test('should add a filter', async () => { + it('should add a filter', async () => { + const user = userEvent.setup(); const saveIndexPattern = jest.fn(async () => {}); - const component = shallow( - {}} - saveIndexPattern={saveIndexPattern} - /> - ); - - await component.instance().onAddFilter('na*'); - component.update(); // We are not calling `.setState` directly so we need to re-render + + const { indexPattern } = renderSourceFiltersTable({ + indexPattern: createDataView([{ value: 'tim*' }]), + saveIndexPattern, + }); + + await user.type(screen.getByTestId('fieldFilterInput'), 'na*'); + await user.click(screen.getByText('Add')); expect(saveIndexPattern).toBeCalled(); - expect(component).toMatchSnapshot(); + expect(await screen.findByText('na*')).toBeVisible(); + + expect(indexPattern.sourceFilters).toEqual([{ value: 'tim*' }, { value: 'na*' }]); }); - test('should update a filter', async () => { + it('should update a filter', async () => { + const user = userEvent.setup(); const saveIndexPattern = jest.fn(async () => {}); - const component = shallow( - {}} - saveIndexPattern={saveIndexPattern} - /> - ); - - await component.instance().saveFilter({ clientId: 'tim*', value: 'ti*' }); - component.update(); // We are not calling `.setState` directly so we need to re-render + + const { indexPattern } = renderSourceFiltersTable({ + indexPattern: createDataView([{ value: 'tim*' }]), + saveIndexPattern, + }); + + await user.click(screen.getByTestId('edit_filter-tim*')); + await user.clear(screen.getByTestId('filter_input_tim*')); + await user.type(screen.getByTestId('filter_input_tim*'), 'ti*'); + await user.click(screen.getByTestId('save_filter-tim*')); expect(saveIndexPattern).toBeCalled(); - expect(component).toMatchSnapshot(); + expect(await screen.findByText('ti*')).toBeVisible(); + expect(indexPattern.sourceFilters).toEqual([{ value: 'ti*', clientId: 1 }]); }); }); From bd77da3920e63c41a4f385ea3a6751dbcb301368 Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Mon, 4 May 2026 17:43:48 +0200 Subject: [PATCH 10/14] Convert confirmation_modal tests --- .../confirmation_modal.test.tsx.snap | 18 ------------------ .../confirmation_modal.test.tsx | 16 ++++++++-------- 2 files changed, 8 insertions(+), 26 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap deleted file mode 100644 index f83f6976f0419..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/__snapshots__/confirmation_modal.test.tsx.snap +++ /dev/null @@ -1,18 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DeleteScritpedFieldConfirmationModal should render normally 1`] = ` - -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/confirmation_modal.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/confirmation_modal.test.tsx index a1aa33a0e885f..03ea124025220 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/confirmation_modal.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/confirmation_modal/confirmation_modal.test.tsx @@ -8,20 +8,20 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; - import { DeleteScritpedFieldConfirmationModal } from './confirmation_modal'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; describe('DeleteScritpedFieldConfirmationModal', () => { - test('should render normally', () => { - const component = shallow( + it('should render normally', () => { + renderWithI18n( {}} - hideDeleteConfirmationModal={() => {}} + deleteField={jest.fn()} + field={{ lang: '', name: '', script: '' }} + hideDeleteConfirmationModal={jest.fn()} /> ); - expect(component).toMatchSnapshot(); + expect(screen.getByText("Delete scripted field ''?")).toBeVisible(); }); }); From 019ff9f5d7337831276f258af3326723206a48a1 Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Wed, 6 May 2026 10:34:15 +0200 Subject: [PATCH 11/14] Convert scripted_fields_table table tests --- .../components/table/table.test.tsx | 2 +- .../table/__snapshots__/table.test.tsx.snap | 108 --------- .../components/table/table.test.tsx | 219 ++++++++++-------- .../source_filters_table.test.tsx | 2 +- 4 files changed, 129 insertions(+), 202 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index 38f7f053ed2f7..4066342f3152d 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -144,7 +144,7 @@ const renderTable = ({ describe('Table', () => { beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); // Silent EUI warnings during tests }); afterEach(() => { diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap deleted file mode 100644 index 7905ba607b07b..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap +++ /dev/null @@ -1,108 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Table should render normally 1`] = ` - -`; - -exports[`Table should render the format 1`] = ` - - string - -`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx index 539514a8fac8e..32a6cc160f6f5 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx @@ -7,121 +7,156 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { ScriptedFieldItem } from '../../types'; import React from 'react'; -import { shallow } from 'enzyme'; - +import { createStubDataView } from '@kbn/data-views-plugin/public/data_views/data_view.stub'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { screen } from '@testing-library/react'; import { TableWithoutPersist as Table } from './table'; -import type { ScriptedFieldItem } from '../../types'; -import type { DataView } from '@kbn/data-views-plugin/public'; - -const getIndexPatternMock = (mockedFields: any = {}) => ({ ...mockedFields } as DataView); - -const items: ScriptedFieldItem[] = [ - { name: '1', lang: 'painless', script: '', isUserEditable: true }, - { name: '2', lang: 'painless', script: '', isUserEditable: false }, -]; +import { userEvent } from '@testing-library/user-event'; + +const createScriptedField = ({ + displayName, + ...field +}: ScriptedFieldItem & { displayName: string }) => ({ + displayName, + ...field, +}); -const baseProps = { +const baseProps: Pick, 'euiTablePersist'> = { euiTablePersist: { + onTableChange: jest.fn(), pageSize: 10, - onTableChange: () => {}, - sorting: { sort: { direction: 'asc' as const, field: 'name' as const } }, + sorting: { sort: { direction: 'asc', field: 'name' } }, }, }; -describe('Table', () => { - let indexPattern: DataView; - - beforeEach(() => { - indexPattern = getIndexPatternMock({ - fieldFormatMap: { - Elastic: { - type: { - title: 'string', - }, +const indexPattern = Object.assign( + createStubDataView({ + spec: { + title: 'test-data-view', + }, + }), + { + fieldFormatMap: { + Elastic: { + type: { + title: 'string', }, }, - }); + }, + } +); + +const items = [ + createScriptedField({ + displayName: 'Elastic', + isUserEditable: true, + lang: 'painless', + name: 'Elastic', + script: "emit('one')", + }), + createScriptedField({ + displayName: 'ReadonlyScriptedField', + isUserEditable: false, + lang: 'painless', + name: 'ReadonlyScriptedField', + script: "emit('two')", + }), +]; + +const getActionButton = (fieldName: string, buttonIndex: number) => { + const button = getRowByText(fieldName).querySelectorAll('button')[buttonIndex]; + + if (!button) { + throw new Error(`Unable to find action button ${buttonIndex} for field ${fieldName}`); + } + + return button; +}; + +const getRowByText = (text: string) => { + const row = screen.getByText(text).closest('tr'); + + if (!row) throw new Error(`Unable to find row for ${text}`); + + return row; +}; + +const renderTable = ({ + deleteField = jest.fn(), + editField = jest.fn(), + tableItems = items, +}: { + deleteField?: React.ComponentProps['deleteField']; + editField?: React.ComponentProps['editField']; + tableItems?: React.ComponentProps['items']; +} = {}) => { + renderWithI18n( +
+ ); + + return { deleteField, editField }; +}; + +describe('Table', () => { + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); // Silent EUI warnings during tests + }); + + afterEach(() => { + jest.restoreAllMocks(); }); - test('should render normally', () => { - const component = shallow( -
{}} - deleteField={() => {}} - /> - ); - - expect(component).toMatchSnapshot(); + it('should render normally', () => { + renderTable(); + + expect(screen.getByText('Elastic')).toBeVisible(); + expect(screen.getByText('ReadonlyScriptedField')).toBeVisible(); + expect(screen.getAllByText('painless')).toHaveLength(2); + expect(screen.getByText("emit('one')")).toBeVisible(); + expect(screen.getByText("emit('two')")).toBeVisible(); }); - test('should render the format', () => { - const component = shallow( -
{}} - deleteField={() => {}} - /> - ); - - const formatTableCell = shallow(component.prop('columns')[3].render('Elastic')); - expect(formatTableCell).toMatchSnapshot(); + it('should render the format', () => { + renderTable(); + + expect(getRowByText('Elastic')).toHaveTextContent('string'); }); - test('should allow edits', () => { + it('should allow edits', async () => { + const user = userEvent.setup(); const editField = jest.fn(); - const component = shallow( -
{}} - /> - ); - - // Click the delete button - component.prop('columns')[4].actions[0].onClick(); - expect(editField).toBeCalled(); + renderTable({ editField }); + + await user.click(getActionButton('Elastic', 0)); + + expect(editField).toHaveBeenCalled(); }); - test('should allow deletes', () => { + it('should allow deletes', async () => { + const user = userEvent.setup(); const deleteField = jest.fn(); - const component = shallow( -
{}} - deleteField={deleteField} - /> - ); - - // Click the delete button - component.prop('columns')[4].actions[1].onClick(); - expect(deleteField).toBeCalled(); + renderTable({ deleteField }); + + await user.click(getActionButton('Elastic', 1)); + + expect(deleteField).toHaveBeenCalled(); }); - test('should not allow edit or deletion for user with only read access', () => { - const component = shallow( -
{}} - deleteField={() => {}} - /> - ); - const editAvailable = component.prop('columns')[4].actions[0].available(items[1]); - const deleteAvailable = component.prop('columns')[4].actions[1].available(items[1]); - expect(editAvailable).toBeFalsy(); - expect(deleteAvailable).toBeFalsy(); + it('should not allow edit or deletion for user with only read access', () => { + renderTable({ tableItems: [items[1]] }); + + expect(screen.getByText('ReadonlyScriptedField')).toBeVisible(); + expect(screen.queryByText('Edit')).not.toBeInTheDocument(); + expect(screen.queryByText('Delete')).not.toBeInTheDocument(); }); }); diff --git a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx index 56adc277eb913..619f97609a635 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.test.tsx @@ -73,7 +73,7 @@ const renderSourceFiltersTable = ({ describe('SourceFiltersTable', () => { beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); // Silent EUI warnings during tests }); afterEach(() => { From 8024f04564c227c62d918dad006ad72eedb14382 Mon Sep 17 00:00:00 2001 From: Milosz Radzynski Date: Thu, 7 May 2026 10:03:34 +0200 Subject: [PATCH 12/14] Convert the field_editor unit tests --- .../__snapshots__/field_editor.test.tsx.snap | 1455 ----------------- .../field_editor/field_editor.test.tsx | 498 +++--- .../public/components/test_utils.tsx | 31 - 3 files changed, 249 insertions(+), 1735 deletions(-) delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap delete mode 100644 src/platform/plugins/shared/data_view_management/public/components/test_utils.tsx diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap b/src/platform/plugins/shared/data_view_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap deleted file mode 100644 index 07a2e6ff0c270..0000000000000 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap +++ /dev/null @@ -1,1455 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FieldEditor should render create new scripted field correctly 1`] = ` - - -

- -

-
- - - - - - - - - - } - label="Custom label" - > - - - - - - - - - - } - label={ - - } - > - - - - - - - } - fullWidth={true} - isInvalid={true} - label="Script" - > - - - - - - doc['some_field'].value - , - } - } - /> - -
- - - -
- - - - - - - - - - - - - - -
- -
-`; - -exports[`FieldEditor should render edit scripted field correctly 1`] = ` - - -

- -

-
- - - - - - - } - label="Custom label" - > - - - - - - - - - - } - label={ - - } - > - - - - - - - - - - - - doc['some_field'].value - , - } - } - /> - -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- -
-`; - -exports[`FieldEditor should show conflict field warning 1`] = ` - - -

- -

-
- - - - - - - -   - - foobar - , - "mappingConflict": - - , - } - } - /> - - } - isInvalid={false} - label="Name" - > - - - - } - label="Custom label" - > - - - - - - - - - - } - label={ - - } - > - - - - - - - } - fullWidth={true} - isInvalid={true} - label="Script" - > - - - - - - doc['some_field'].value - , - } - } - /> - -
- - - -
- - - - - - - - - - - - - - -
- -
-`; - -exports[`FieldEditor should show deprecated lang warning 1`] = ` - - -

- -

-
- - - - - - - } - label="Custom label" - > - - - - -   - - - -   - - testlang - , - "painlessLink": - - , - } - } - /> - - } - label="Language" - > - - - - - - - } - label={ - - } - > - - - - - - - - - - - - doc['some_field'].value - , - } - } - /> - -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - -
- -
-`; - -exports[`FieldEditor should show multiple type field warning with a table containing indices 1`] = ` - - -

- -

-
- - - - - - - -   - - foobar - , - "mappingConflict": - - , - } - } - /> - - } - isInvalid={false} - label="Name" - > - - - - } - label="Custom label" - > - - - - - - - - -
- - - } - > - - - - - -
- - } - label={ - - } - > - - - - - - - } - fullWidth={true} - isInvalid={true} - label="Script" - > - - - - - - doc['some_field'].value - , - } - } - /> - -
- - - -
- - - - - - - - - - - - - - -
- -
-`; diff --git a/src/platform/plugins/shared/data_view_management/public/components/field_editor/field_editor.test.tsx b/src/platform/plugins/shared/data_view_management/public/components/field_editor/field_editor.test.tsx index 0b2a0d7cef33e..1bd00a2f5b5b8 100644 --- a/src/platform/plugins/shared/data_view_management/public/components/field_editor/field_editor.test.tsx +++ b/src/platform/plugins/shared/data_view_management/public/components/field_editor/field_editor.test.tsx @@ -7,318 +7,318 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { DataView, DataViewField, DataViewsContract } from '@kbn/data-views-plugin/public'; -import type { FieldFormatInstanceType } from '@kbn/field-formats-plugin/common'; -import { findTestSubject } from '@elastic/eui/lib/test'; - import type { FieldEdiorProps } from './field_editor'; -import { FieldEditor } from './field_editor'; - -import { mockManagementPlugin } from '../../mocks'; -import { createComponentWithContext } from '../test_utils'; - -jest.mock('@elastic/eui', () => ({ - ...jest.requireActual('@elastic/eui'), - EuiBasicTable: 'eui-basic-table', - EuiButton: 'eui-button', - EuiButtonEmpty: 'eui-button-empty', - EuiCallOut: 'eui-call-out', - EuiCode: 'eui-code', - EuiConfirmModal: 'eui-confirm-modal', - EuiFieldNumber: 'eui-field-number', - EuiFieldText: 'eui-field-text', - EuiFlexGroup: 'eui-flex-group', - EuiFlexItem: 'eui-flex-item', - EuiForm: 'eui-form', - EuiFormRow: 'eui-form-row', - EuiIcon: 'eui-icon', - EuiLink: 'eui-link', - EuiOverlayMask: 'eui-overlay-mask', - EuiSelect: 'eui-select', - EuiSpacer: 'eui-spacer', - EuiText: 'eui-text', - EuiTextArea: 'eui-textArea', - htmlIdGenerator: () => () => 42, - euiPaletteColorBlind: () => ['red'], +import type { DataView, FieldSpec } from '@kbn/data-views-plugin/public'; +import { createStubDataView } from '@kbn/data-views-plugin/public/data_views/data_view.stub'; +import React from 'react'; +import { FieldFormat } from '@kbn/field-formats-plugin/common'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public/context'; +import { render, screen, waitFor, within } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; + +const monacoModuleName = '@kbn/monaco'; + +jest.doMock('@kbn/code-editor', () => ({ + CodeEditor: ({ + height: _height, + languageId: _languageId, + onChange, + value, + width: _width, + ...props + }: { + height: string; + languageId: string; + onChange: (value: string) => void; + value: string; + width: string; + }) => ( +