From fdefb09892769ef15d7e4fefb0d536c23cb64c3a Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Tue, 17 Mar 2026 15:30:49 +0100 Subject: [PATCH 1/9] [Upgrade Assistant] Add ES deprecations page/table unit tests ## Summary Adds RTL unit tests and shared fixtures for the Elasticsearch deprecations page and table behaviors (loading/prompt/errors, refresh, counts, filtering/search, and pagination). ## Test plan - [x] node scripts/check_changes.ts - [x] yarn test:jest x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations - [x] yarn test:type_check --project x-pack/platform/plugins/private/upgrade_assistant/tsconfig.json Made-with: Cursor --- .../__fixtures__/es_deprecations.ts | 122 ++++++++++ .../es_deprecations/es_deprecations.test.tsx | 228 ++++++++++++++++++ .../es_deprecations_table.test.tsx | 217 +++++++++++++++++ 3 files changed, 567 insertions(+) create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts new file mode 100644 index 0000000000000..0f24e05fac0e7 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + ClusterSettingAction, + EnrichedDeprecationInfo, + ESUpgradeStatus, + IndexSettingAction, + MlAction, +} from '../../../../../common/types'; + +export const MOCK_SNAPSHOT_ID = '1'; +export const MOCK_JOB_ID = 'deprecation_check_job'; + +export const mockMlDeprecation = { + level: 'critical', + resolveDuringUpgrade: false, + type: 'ml_settings', + message: 'model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded', + details: + 'model snapshot [%s] for job [%s] supports minimum version [%s] and needs to be at least [%s]', + url: 'doc_url', + correctiveAction: { + type: 'mlSnapshot', + snapshotId: MOCK_SNAPSHOT_ID, + jobId: MOCK_JOB_ID, + } satisfies MlAction, +} satisfies EnrichedDeprecationInfo; + +export const mockIndexSettingDeprecation = { + level: 'warning', + resolveDuringUpgrade: false, + type: 'index_settings', + message: 'Setting [index.routing.allocation.include._tier] is deprecated', + details: 'deprecation details', + url: 'doc_url', + index: 'my_index', + correctiveAction: { + type: 'indexSetting', + deprecatedSettings: ['index.routing.allocation.include._tier'], + } satisfies IndexSettingAction, +} satisfies EnrichedDeprecationInfo; + +export const mockClusterSettingDeprecation = { + level: 'warning', + resolveDuringUpgrade: false, + type: 'cluster_settings', + message: 'Setting [cluster.routing.allocation.require._tier] is deprecated', + details: 'deprecation details', + url: 'doc_url', + correctiveAction: { + type: 'clusterSetting', + deprecatedSettings: ['cluster.routing.allocation.require._tier'], + } satisfies ClusterSettingAction, +} satisfies EnrichedDeprecationInfo; + +export const mockDefaultDeprecation = { + level: 'warning', + resolveDuringUpgrade: false, + type: 'index_settings', + message: 'multi-fields within multi-fields', + details: 'deprecation details', + url: 'doc_url', + index: 'nested_multi-fields', +} satisfies EnrichedDeprecationInfo; + +export const mockReindexDeprecation = { + level: 'critical', + resolveDuringUpgrade: false, + type: 'index_settings', + message: 'Index created before 7.0', + details: 'deprecation details', + url: 'doc_url', + index: 'reindex_index', + correctiveAction: { + type: 'reindex', + metadata: { + isClosedIndex: false, + isFrozenIndex: false, + isInDataStream: false, + }, + }, +} satisfies EnrichedDeprecationInfo; + +export const mockEsDeprecations = { + totalCriticalDeprecations: 2, + migrationsDeprecations: [ + mockMlDeprecation, + mockIndexSettingDeprecation, + mockDefaultDeprecation, + mockReindexDeprecation, + mockClusterSettingDeprecation, + ], + totalCriticalHealthIssues: 0, + enrichedHealthIndicators: [], +} satisfies ESUpgradeStatus; + +export const createEsDeprecations = (numDeprecationsPerType: number): ESUpgradeStatus => { + const repeat = (deprecation: EnrichedDeprecationInfo) => + Array.from({ length: numDeprecationsPerType }, () => deprecation); + + const mlDeprecations = repeat(mockMlDeprecation); + const indexSettingsDeprecations = repeat(mockIndexSettingDeprecation); + const reindexDeprecations = repeat(mockReindexDeprecation); + const defaultDeprecations = repeat(mockDefaultDeprecation); + + return { + totalCriticalDeprecations: mlDeprecations.length + reindexDeprecations.length, + migrationsDeprecations: [ + ...defaultDeprecations, + ...reindexDeprecations, + ...indexSettingsDeprecations, + ...mlDeprecations, + ], + totalCriticalHealthIssues: mockEsDeprecations.totalCriticalHealthIssues, + enrichedHealthIndicators: mockEsDeprecations.enrichedHealthIndicators, + }; +}; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx new file mode 100644 index 0000000000000..1a68ef907d609 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx @@ -0,0 +1,228 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { createMemoryHistory } from 'history'; +import { Router } from '@kbn/shared-ux-router'; + +import type { ResponseError } from '../../../../common/types'; +import { EsDeprecations } from './es_deprecations'; +import { mockEsDeprecations } from './__fixtures__/es_deprecations'; + +const mockBreadcrumbsSetBreadcrumbs = jest.fn(); + +const mockUseLoadEsDeprecations = jest.fn(); +const mockUseLoadRemoteClusters = jest.fn(); + +jest.mock('../../app_context', () => ({ + useAppContext: () => ({ + plugins: { + share: { + url: { + locators: { + get: () => ({ + useUrl: () => '/app/management/data/remote_clusters', + }), + }, + }, + }, + }, + services: { + api: { + useLoadEsDeprecations: () => mockUseLoadEsDeprecations(), + useLoadRemoteClusters: () => mockUseLoadRemoteClusters(), + }, + breadcrumbs: { + setBreadcrumbs: mockBreadcrumbsSetBreadcrumbs, + }, + core: { + docLinks: { + links: { + upgradeAssistant: { + batchReindex: 'https://example.invalid/batch-reindex', + }, + }, + }, + }, + }, + }), +})); + +jest.mock('./es_deprecations_table', () => ({ + EsDeprecationsTable: () =>
, +})); + +describe('EsDeprecations', () => { + const renderPage = () => { + const history = createMemoryHistory(); + return renderWithI18n( + + + + ); + }; + + beforeEach(() => { + mockBreadcrumbsSetBreadcrumbs.mockClear(); + mockUseLoadEsDeprecations.mockReset(); + mockUseLoadRemoteClusters.mockReset(); + }); + + it('shows loading state', () => { + mockUseLoadEsDeprecations.mockReturnValue({ + data: undefined, + isLoading: true, + error: undefined, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: [] }); + + renderPage(); + + expect(screen.getByText('Loading deprecation issues…')).toBeInTheDocument(); + }); + + it('shows no deprecations prompt', async () => { + mockUseLoadEsDeprecations.mockReturnValue({ + data: { ...mockEsDeprecations, migrationsDeprecations: [] }, + isLoading: false, + error: undefined, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: [] }); + + renderPage(); + + expect(await screen.findByTestId('noDeprecationsPrompt')).toBeInTheDocument(); + }); + + it('renders remote clusters callout', async () => { + mockUseLoadEsDeprecations.mockReturnValue({ + data: mockEsDeprecations, + isLoading: false, + error: undefined, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: ['test_remote_cluster'] }); + + renderPage(); + + expect(await screen.findByTestId('remoteClustersWarningCallout')).toBeInTheDocument(); + expect(screen.getByTestId('remoteClustersLink')).toHaveAttribute( + 'href', + '/app/management/data/remote_clusters' + ); + }); + + it('shows critical and warning deprecations count', async () => { + mockUseLoadEsDeprecations.mockReturnValue({ + data: mockEsDeprecations, + isLoading: false, + error: undefined, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: [] }); + + renderPage(); + + expect(await screen.findByTestId('criticalDeprecationsCount')).toHaveTextContent('Critical: 2'); + expect(screen.getByTestId('warningDeprecationsCount')).toHaveTextContent('Warning: 3'); + }); + + it('handles 403', async () => { + const error: ResponseError = { + statusCode: 403, + message: 'Forbidden', + }; + + mockUseLoadEsDeprecations.mockReturnValue({ + data: undefined, + isLoading: false, + error, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: [] }); + + renderPage(); + + expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( + 'You are not authorized to view Elasticsearch deprecation issues.' + ); + }); + + it('shows upgraded message when all nodes have been upgraded', async () => { + const error: ResponseError = { + statusCode: 426, + message: 'There are some nodes running a different version of Elasticsearch', + attributes: { + allNodesUpgraded: true, + }, + }; + + mockUseLoadEsDeprecations.mockReturnValue({ + data: undefined, + isLoading: false, + error, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: [] }); + + renderPage(); + + expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( + 'All Elasticsearch nodes have been upgraded.' + ); + }); + + it('shows partially upgraded warning when nodes are running different versions', async () => { + const error: ResponseError = { + statusCode: 426, + message: 'There are some nodes running a different version of Elasticsearch', + attributes: { + allNodesUpgraded: false, + }, + }; + + mockUseLoadEsDeprecations.mockReturnValue({ + data: undefined, + isLoading: false, + error, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: [] }); + + renderPage(); + + expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( + 'Upgrade Kibana to the same version as your Elasticsearch cluster. One or more nodes in the cluster is running a different version than Kibana.' + ); + }); + + it('handles generic error', async () => { + const error: ResponseError = { + statusCode: 500, + message: 'Internal server error', + }; + + mockUseLoadEsDeprecations.mockReturnValue({ + data: undefined, + isLoading: false, + error, + resendRequest: jest.fn(), + }); + mockUseLoadRemoteClusters.mockReturnValue({ data: [] }); + + renderPage(); + + expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( + 'Could not retrieve Elasticsearch deprecation issues.' + ); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx new file mode 100644 index 0000000000000..e79a89a5f0cad --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx @@ -0,0 +1,217 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen, waitFor, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { EnrichedDeprecationInfo } from '../../../../common/types'; +import { createEsDeprecations } from './__fixtures__/es_deprecations'; + +jest.mock('../../app_context', () => ({ + useAppContext: () => ({ + kibanaVersionInfo: { + currentMajor: 8, + currentMinor: 0, + currentPatch: 0, + }, + services: { + api: { + useLoadMlUpgradeMode: () => ({ data: { mlUpgradeModeEnabled: false } }), + }, + }, + }), +})); + +jest.mock('./deprecation_types', () => { + const TestRow = ({ + deprecation, + index, + }: { + deprecation: EnrichedDeprecationInfo; + index: number; + }) => ( + + {deprecation.message} + + ); + + return { + MlSnapshotsTableRow: TestRow, + DefaultTableRow: TestRow, + IndexSettingsTableRow: TestRow, + IndexTableRow: TestRow, + ClusterSettingsTableRow: TestRow, + HealthIndicatorTableRow: TestRow, + DataStreamTableRow: TestRow, + }; +}); + +describe('EsDeprecationsTable', () => { + const reloadMock = jest.fn(); + + const renderTable = async (deprecations: EnrichedDeprecationInfo[]) => { + const { EsDeprecationsTable } = await import('./es_deprecations_table'); + renderWithI18n(); + }; + + const getPaginationItemsCount = () => { + const pagination = screen.getByTestId('esDeprecationsPagination'); + return pagination.querySelectorAll('.euiPagination__item').length; + }; + + it('calls reload when refresh is clicked', async () => { + const user = userEvent.setup(); + const { migrationsDeprecations } = createEsDeprecations(1); + reloadMock.mockClear(); + + await renderTable(migrationsDeprecations); + + await user.click(screen.getByTestId('refreshButton')); + expect(reloadMock).toHaveBeenCalledTimes(1); + }); + + it('shows the correct number of pages and deprecations per page', async () => { + const user = userEvent.setup(); + const { migrationsDeprecations } = createEsDeprecations(20); + + await renderTable(migrationsDeprecations); + + expect(getPaginationItemsCount()).toEqual(Math.ceil(migrationsDeprecations.length / 50)); + expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength(50); + + await user.click(screen.getByTestId('pagination-button-1')); + + await waitFor(() => { + expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( + migrationsDeprecations.length - 50 + ); + }); + }); + + it('allows the number of viewable rows to change', async () => { + const user = userEvent.setup(); + const { migrationsDeprecations } = createEsDeprecations(20); + + await renderTable(migrationsDeprecations); + + await user.click(screen.getByTestId('tablePaginationPopoverButton')); + const rowsPerPageButton = await screen.findByTestId('tablePagination-100-rows'); + await user.click(rowsPerPageButton); + + await waitFor(() => { + expect(getPaginationItemsCount()).toEqual(Math.ceil(migrationsDeprecations.length / 100)); + expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( + migrationsDeprecations.length + ); + }); + }); + + it('updates pagination when filters change', async () => { + const user = userEvent.setup(); + const { migrationsDeprecations } = createEsDeprecations(20); + const criticalDeprecations = migrationsDeprecations.filter((d) => d.level === 'critical'); + + await renderTable(migrationsDeprecations); + + const searchBar = screen.getByTestId('searchBarContainer'); + const filterButtons = searchBar.querySelectorAll('button.euiFilterButton'); + await user.click(filterButtons[0]); + + const criticalFilterButton = await waitFor(() => { + const el = document.body.querySelector('.euiSelectableListItem[title="Critical"]'); + expect(el).not.toBeNull(); + return el; + }); + + expect(criticalFilterButton).not.toBeNull(); + if (criticalFilterButton) { + await user.click(criticalFilterButton); + } + + await waitFor(() => { + expect(getPaginationItemsCount()).toEqual(Math.ceil(criticalDeprecations.length / 50)); + expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( + Math.min(criticalDeprecations.length, 50) + ); + }); + }); + + it('updates pagination on search', async () => { + const { migrationsDeprecations } = createEsDeprecations(20); + const reindexDeprecations = migrationsDeprecations.filter( + (d) => d.correctiveAction?.type === 'reindex' + ); + + await renderTable(migrationsDeprecations); + + const input = within(screen.getByTestId('searchBarContainer')).getByRole('searchbox'); + fireEvent.change(input, { target: { value: 'Index created before 7.0' } }); + fireEvent.keyUp(input, { target: { value: 'Index created before 7.0' } }); + + await waitFor(() => { + expect(getPaginationItemsCount()).toEqual(Math.ceil(reindexDeprecations.length / 50)); + expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( + Math.min(reindexDeprecations.length, 50) + ); + }); + }); + + it('maintains correct row state across pagination', async () => { + const user = userEvent.setup(); + const { migrationsDeprecations } = createEsDeprecations(20); + + await renderTable(migrationsDeprecations); + + const getFirstRowMessageCellText = () => { + const row = screen.getAllByTestId('deprecationTableRow')[0]; + const messageCell = row.querySelector('[data-test-subj$="-message"]'); + expect(messageCell).not.toBeNull(); + return messageCell?.textContent; + }; + + expect(getPaginationItemsCount()).toBeGreaterThan(1); + + const firstDeprecationMessagePage1 = getFirstRowMessageCellText(); + + await user.click(screen.getByTestId('pagination-button-1')); + await waitFor(() => { + expect(getFirstRowMessageCellText()).not.toEqual(firstDeprecationMessagePage1); + }); + + await user.click(screen.getByTestId('pagination-button-0')); + await waitFor(() => { + expect(getFirstRowMessageCellText()).toEqual(firstDeprecationMessagePage1); + }); + }); + + it('shows error for invalid search queries', async () => { + const { migrationsDeprecations } = createEsDeprecations(1); + await renderTable(migrationsDeprecations); + + const input = within(screen.getByTestId('searchBarContainer')).getByRole('searchbox'); + fireEvent.change(input, { target: { value: '%' } }); + fireEvent.keyUp(input, { target: { value: '%' } }); + + expect(await screen.findByTestId('invalidSearchQueryMessage')).toBeInTheDocument(); + }); + + it('shows message when search query does not return results', async () => { + const { migrationsDeprecations } = createEsDeprecations(1); + await renderTable(migrationsDeprecations); + + const input = within(screen.getByTestId('searchBarContainer')).getByRole('searchbox'); + fireEvent.change(input, { target: { value: 'foobarbaz' } }); + fireEvent.keyUp(input, { target: { value: 'foobarbaz' } }); + + expect(await screen.findByTestId('noDeprecationsRow')).toHaveTextContent( + 'No Elasticsearch deprecation issues found' + ); + }); +}); From 18daeabf4fac63f3611bd3b4bfe29a47a3ef11d3 Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Tue, 17 Mar 2026 15:31:12 +0100 Subject: [PATCH 2/9] [Upgrade Assistant] Add ES deprecation type unit tests ## Summary Adds unit tests for shared low-disk callout and for ES deprecation type row/flyout behaviors (default, index settings, cluster settings, and ML snapshots). ## Test plan - [x] node scripts/check_changes.ts - [x] yarn test:jest x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations - [x] yarn test:type_check --project x-pack/platform/plugins/private/upgrade_assistant/tsconfig.json Made-with: Cursor --- .../common/nodes_low_disk_space.test.tsx | 34 ++++ .../cluster_settings/flyout.test.tsx | 112 ++++++++++++ .../cluster_settings/table_row.test.tsx | 144 ++++++++++++++++ .../deprecation_types/default/flyout.test.tsx | 31 ++++ .../default/table_row.test.tsx | 58 +++++++ .../index_settings/flyout.test.tsx | 112 ++++++++++++ .../index_settings/table_row.test.tsx | 146 ++++++++++++++++ .../ml_snapshots/flyout.test.tsx | 161 ++++++++++++++++++ 8 files changed, 798 insertions(+) create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/common/nodes_low_disk_space.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/flyout.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/common/nodes_low_disk_space.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/common/nodes_low_disk_space.test.tsx new file mode 100644 index 0000000000000..958920417946d --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/common/nodes_low_disk_space.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { screen, within } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import { NodesLowSpaceCallOut } from './nodes_low_disk_space'; + +describe('NodesLowSpaceCallOut', () => { + it('renders impacted nodes list', () => { + renderWithI18n( + + ); + + const callout = screen.getByTestId('lowDiskSpaceCallout'); + expect(callout).toHaveTextContent('Nodes with low disk space'); + + const items = within(callout).getAllByTestId('impactedNodeListItem'); + expect(items).toHaveLength(2); + expect(items[0]).toHaveTextContent('node-1 (25% available)'); + expect(items[1]).toHaveTextContent('node-2 (10% available)'); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx new file mode 100644 index 0000000000000..d3e276d108136 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { ResponseError } from '../../../../../../common/types'; +import { mockClusterSettingDeprecation } from '../../__fixtures__/es_deprecations'; +import { RemoveClusterSettingsFlyout } from './flyout'; + +jest.mock('../../../../lib/ui_metric', () => ({ + uiMetricService: { + trackUiMetric: jest.fn(), + }, + UIM_CLUSTER_SETTINGS_DELETE_CLICK: 'UIM_CLUSTER_SETTINGS_DELETE_CLICK', +})); + +describe('RemoveClusterSettingsFlyout', () => { + const closeFlyout = jest.fn(); + const removeClusterSettings = jest.fn, [settings: string[]]>(); + + beforeEach(() => { + closeFlyout.mockClear(); + removeClusterSettings.mockClear(); + }); + + it('renders deprecation details and prompt', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('flyoutTitle')).toHaveTextContent( + mockClusterSettingDeprecation.message + ); + expect(screen.getByTestId('documentationLink')).toHaveAttribute( + 'href', + mockClusterSettingDeprecation.url + ); + expect(screen.getByTestId('removeClusterSettingsPrompt')).toBeInTheDocument(); + expect(screen.getByTestId('deleteClusterSettingsButton')).toHaveTextContent( + 'Remove deprecated settings' + ); + }); + + it('calls removeClusterSettings with deprecated settings', () => { + renderWithI18n( + + ); + + fireEvent.click(screen.getByTestId('deleteClusterSettingsButton')); + expect(removeClusterSettings).toHaveBeenCalledWith([ + 'cluster.routing.allocation.require._tier', + ]); + }); + + it('hides prompt and action button when resolved', () => { + renderWithI18n( + + ); + + expect(screen.queryByTestId('removeClusterSettingsPrompt')).toBeNull(); + expect(screen.queryByTestId('deleteClusterSettingsButton')).toBeNull(); + expect(screen.getByTestId('resolvedDeprecationBadge')).toBeInTheDocument(); + }); + + it('shows error callout and changes button label on failure', () => { + const error: ResponseError = { + statusCode: 500, + message: 'Remove cluster settings error', + }; + + renderWithI18n( + + ); + + expect(screen.getByTestId('deleteClusterSettingsError')).toHaveTextContent( + 'Error deleting cluster settings' + ); + expect(screen.getByTestId('deleteClusterSettingsError')).toHaveTextContent( + 'Remove cluster settings error' + ); + expect(screen.getByTestId('deleteClusterSettingsButton')).toHaveTextContent( + 'Retry removing deprecated settings' + ); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx new file mode 100644 index 0000000000000..145bf9a31b7ec --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { act, fireEvent, screen, waitFor, within } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { ResponseError } from '../../../../../../common/types'; +import type { DeprecationTableColumns } from '../../../types'; +import { mockClusterSettingDeprecation } from '../../__fixtures__/es_deprecations'; +import { ClusterSettingsTableRow } from './table_row'; + +type UpdateClusterSettings = (settings: string[]) => Promise<{ error: ResponseError | null }>; + +const mockUpdateClusterSettings = jest.fn< + ReturnType, + Parameters +>(); + +const mockAddContent = jest.fn< + void, + [ + { + id: string; + Component: React.ComponentType; + props: { + removeClusterSettings: (settings: string[]) => Promise; + }; + } + ] +>(); +const mockRemoveContent = jest.fn(); + +jest.mock('../../../../app_context', () => ({ + useAppContext: () => ({ + services: { + api: { + updateClusterSettings: (...args: Parameters) => + mockUpdateClusterSettings(...args), + }, + }, + }), +})); + +jest.mock('../../../../../shared_imports', () => ({ + GlobalFlyout: { + useGlobalFlyout: () => ({ + addContent: (...args: Parameters) => mockAddContent(...args), + removeContent: (...args: Parameters) => mockRemoveContent(...args), + }), + }, +})); + +describe('ClusterSettingsTableRow', () => { + const rowFieldNames: DeprecationTableColumns[] = ['correctiveAction', 'actions']; + + beforeEach(() => { + mockUpdateClusterSettings.mockReset(); + mockAddContent.mockClear(); + mockRemoveContent.mockClear(); + }); + + const renderRow = () => + renderWithI18n( + + + + +
+ ); + + it('opens flyout when action link is clicked', async () => { + renderRow(); + + fireEvent.click(screen.getByTestId('deprecation-clusterSetting')); + + await waitFor(() => { + expect(mockAddContent).toHaveBeenCalled(); + }); + + expect(mockAddContent.mock.calls[0][0].id).toBe('clusterSettingsFlyout'); + }); + + it('updates resolution cell on successful settings removal', async () => { + mockUpdateClusterSettings.mockResolvedValue({ error: null }); + renderRow(); + + fireEvent.click(screen.getByTestId('deprecation-clusterSetting')); + await waitFor(() => { + expect(mockAddContent).toHaveBeenCalled(); + }); + + const { removeClusterSettings } = mockAddContent.mock.calls[0][0].props; + await act(async () => { + await removeClusterSettings(['cluster.routing.allocation.require._tier']); + }); + + expect(mockUpdateClusterSettings).toHaveBeenCalledWith([ + 'cluster.routing.allocation.require._tier', + ]); + expect(mockRemoveContent).toHaveBeenCalledWith('clusterSettingsFlyout'); + + const resolutionCell = within( + screen.getByTestId('clusterSettingsTableCell-correctiveAction') + ).getByTestId('clusterSettingsResolutionStatusCell'); + + await waitFor(() => { + expect(resolutionCell).toHaveTextContent('Deprecated settings removed'); + }); + }); + + it('updates resolution cell on failed settings removal', async () => { + const error: ResponseError = { statusCode: 500, message: 'Remove cluster settings error' }; + mockUpdateClusterSettings.mockResolvedValue({ error }); + renderRow(); + + fireEvent.click(screen.getByTestId('deprecation-clusterSetting')); + await waitFor(() => { + expect(mockAddContent).toHaveBeenCalled(); + }); + + const { removeClusterSettings } = mockAddContent.mock.calls[0][0].props; + await act(async () => { + await removeClusterSettings(['cluster.routing.allocation.require._tier']); + }); + + const resolutionCell = within( + screen.getByTestId('clusterSettingsTableCell-correctiveAction') + ).getByTestId('clusterSettingsResolutionStatusCell'); + + await waitFor(() => { + expect(resolutionCell).toHaveTextContent('Settings removal failed'); + }); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/flyout.test.tsx new file mode 100644 index 0000000000000..3203be91cdefc --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/flyout.test.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import { mockDefaultDeprecation } from '../../__fixtures__/es_deprecations'; +import { DefaultDeprecationFlyout } from './flyout'; + +describe('DefaultDeprecationFlyout', () => { + it('renders deprecation details', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('flyoutTitle')).toHaveTextContent(mockDefaultDeprecation.message); + expect(screen.getByTestId('documentationLink')).toHaveAttribute( + 'href', + mockDefaultDeprecation.url + ); + expect(screen.getByTestId('flyoutDescription')).toHaveTextContent( + String(mockDefaultDeprecation.index) + ); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx new file mode 100644 index 0000000000000..c6c559bdf4f4d --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { DeprecationTableColumns } from '../../../types'; +import { mockDefaultDeprecation } from '../../__fixtures__/es_deprecations'; +import { DefaultTableRow } from './table_row'; + +const mockAddContent = jest.fn(); +const mockRemoveContent = jest.fn(); + +jest.mock('../../../../../shared_imports', () => ({ + GlobalFlyout: { + useGlobalFlyout: () => ({ + addContent: (...args: Parameters) => mockAddContent(...args), + removeContent: (...args: Parameters) => mockRemoveContent(...args), + }), + }, +})); + +describe('DefaultTableRow', () => { + const rowFieldNames: DeprecationTableColumns[] = ['message', 'actions']; + + beforeEach(() => { + mockAddContent.mockClear(); + mockRemoveContent.mockClear(); + }); + + it('opens flyout when action icon is clicked', async () => { + renderWithI18n( + + + + +
+ ); + + fireEvent.click(screen.getByTestId('deprecation-default')); + + await waitFor(() => { + expect(mockAddContent).toHaveBeenCalled(); + }); + + expect(mockAddContent.mock.calls[0][0].id).toBe('deprecationDetails'); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx new file mode 100644 index 0000000000000..db4435e645442 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { ResponseError } from '../../../../../../common/types'; +import { mockIndexSettingDeprecation } from '../../__fixtures__/es_deprecations'; +import { RemoveIndexSettingsFlyout } from './flyout'; + +jest.mock('../../../../lib/ui_metric', () => ({ + uiMetricService: { + trackUiMetric: jest.fn(), + }, + UIM_INDEX_SETTINGS_DELETE_CLICK: 'UIM_INDEX_SETTINGS_DELETE_CLICK', +})); + +describe('RemoveIndexSettingsFlyout', () => { + const closeFlyout = jest.fn(); + const removeIndexSettings = jest.fn, [index: string, settings: string[]]>(); + + beforeEach(() => { + closeFlyout.mockClear(); + removeIndexSettings.mockClear(); + }); + + it('renders deprecation details and prompt', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('flyoutTitle')).toHaveTextContent( + mockIndexSettingDeprecation.message + ); + expect(screen.getByTestId('documentationLink')).toHaveAttribute( + 'href', + mockIndexSettingDeprecation.url + ); + expect(screen.getByTestId('removeSettingsPrompt')).toBeInTheDocument(); + expect(screen.getByTestId('deleteSettingsButton')).toHaveTextContent( + 'Remove deprecated settings' + ); + }); + + it('calls removeIndexSettings with index and settings', () => { + renderWithI18n( + + ); + + fireEvent.click(screen.getByTestId('deleteSettingsButton')); + expect(removeIndexSettings).toHaveBeenCalledWith('my_index', [ + 'index.routing.allocation.include._tier', + ]); + }); + + it('hides prompt and action button when resolved', () => { + renderWithI18n( + + ); + + expect(screen.queryByTestId('removeSettingsPrompt')).toBeNull(); + expect(screen.queryByTestId('deleteSettingsButton')).toBeNull(); + expect(screen.getByTestId('resolvedDeprecationBadge')).toBeInTheDocument(); + }); + + it('shows error callout and changes button label on failure', () => { + const error: ResponseError = { + statusCode: 500, + message: 'Remove index settings error', + }; + + renderWithI18n( + + ); + + expect(screen.getByTestId('deleteSettingsError')).toHaveTextContent( + 'Error deleting index settings' + ); + expect(screen.getByTestId('deleteSettingsError')).toHaveTextContent( + 'Remove index settings error' + ); + expect(screen.getByTestId('deleteSettingsButton')).toHaveTextContent( + 'Retry removing deprecated settings' + ); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx new file mode 100644 index 0000000000000..d5c0088b68468 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { act, fireEvent, screen, waitFor, within } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { ResponseError } from '../../../../../../common/types'; +import type { DeprecationTableColumns } from '../../../types'; +import { mockIndexSettingDeprecation } from '../../__fixtures__/es_deprecations'; +import { IndexSettingsTableRow } from './table_row'; + +type UpdateIndexSettings = ( + index: string, + settings: string[] +) => Promise<{ error: ResponseError | null }>; + +const mockUpdateIndexSettings = jest.fn< + ReturnType, + Parameters +>(); + +const mockAddContent = jest.fn< + void, + [ + { + id: string; + Component: React.ComponentType; + props: { + removeIndexSettings: (index: string, settings: string[]) => Promise; + }; + } + ] +>(); +const mockRemoveContent = jest.fn(); + +jest.mock('../../../../app_context', () => ({ + useAppContext: () => ({ + services: { + api: { + updateIndexSettings: (...args: Parameters) => + mockUpdateIndexSettings(...args), + }, + }, + }), +})); + +jest.mock('../../../../../shared_imports', () => ({ + GlobalFlyout: { + useGlobalFlyout: () => ({ + addContent: (...args: Parameters) => mockAddContent(...args), + removeContent: (...args: Parameters) => mockRemoveContent(...args), + }), + }, +})); + +describe('IndexSettingsTableRow', () => { + const rowFieldNames: DeprecationTableColumns[] = ['correctiveAction', 'actions']; + + beforeEach(() => { + mockUpdateIndexSettings.mockReset(); + mockAddContent.mockClear(); + mockRemoveContent.mockClear(); + }); + + const renderRow = () => + renderWithI18n( + + + + +
+ ); + + it('opens flyout when action link is clicked', async () => { + renderRow(); + + fireEvent.click(screen.getByTestId('deprecation-indexSetting')); + + await waitFor(() => { + expect(mockAddContent).toHaveBeenCalled(); + }); + + expect(mockAddContent.mock.calls[0][0].id).toBe('indexSettingsFlyout'); + }); + + it('updates resolution cell on successful settings removal', async () => { + mockUpdateIndexSettings.mockResolvedValue({ error: null }); + renderRow(); + + fireEvent.click(screen.getByTestId('deprecation-indexSetting')); + + await waitFor(() => { + expect(mockAddContent).toHaveBeenCalled(); + }); + + const { removeIndexSettings } = mockAddContent.mock.calls[0][0].props; + await act(async () => { + await removeIndexSettings('my_index', ['index.routing.allocation.include._tier']); + }); + + expect(mockUpdateIndexSettings).toHaveBeenCalledWith('my_index', [ + 'index.routing.allocation.include._tier', + ]); + expect(mockRemoveContent).toHaveBeenCalledWith('indexSettingsFlyout'); + + const resolutionCell = within( + screen.getByTestId('indexSettingsTableCell-correctiveAction') + ).getByTestId('indexSettingsResolutionStatusCell'); + await waitFor(() => { + expect(resolutionCell).toHaveTextContent('Deprecated settings removed'); + }); + }); + + it('updates resolution cell on failed settings removal', async () => { + const error: ResponseError = { statusCode: 500, message: 'Remove index settings error' }; + mockUpdateIndexSettings.mockResolvedValue({ error }); + renderRow(); + + fireEvent.click(screen.getByTestId('deprecation-indexSetting')); + await waitFor(() => { + expect(mockAddContent).toHaveBeenCalled(); + }); + + const { removeIndexSettings } = mockAddContent.mock.calls[0][0].props; + await act(async () => { + await removeIndexSettings('my_index', ['index.routing.allocation.include._tier']); + }); + + const resolutionCell = within( + screen.getByTestId('indexSettingsTableCell-correctiveAction') + ).getByTestId('indexSettingsResolutionStatusCell'); + await waitFor(() => { + expect(resolutionCell).toHaveTextContent('Settings removal failed'); + }); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx new file mode 100644 index 0000000000000..93502e2a6c6af --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { ResponseError } from '../../../../../../common/types'; +import { + mockMlDeprecation, + MOCK_JOB_ID, + MOCK_SNAPSHOT_ID, +} from '../../__fixtures__/es_deprecations'; +import type { SnapshotState } from './use_snapshot_state'; +import { FixSnapshotsFlyout } from './flyout'; + +jest.mock('../../../../app_context', () => ({ + useAppContext: () => ({ + services: { + core: { + docLinks: { + links: { + ml: { + setUpgradeMode: 'https://example.invalid/ml-upgrade-mode', + }, + }, + }, + }, + }, + }), +})); + +jest.mock('../../../../lib/ui_metric', () => ({ + uiMetricService: { + trackUiMetric: jest.fn(), + }, + UIM_ML_SNAPSHOT_UPGRADE_CLICK: 'UIM_ML_SNAPSHOT_UPGRADE_CLICK', + UIM_ML_SNAPSHOT_DELETE_CLICK: 'UIM_ML_SNAPSHOT_DELETE_CLICK', +})); + +describe('FixSnapshotsFlyout', () => { + const closeFlyout = jest.fn(); + const upgradeSnapshot = jest.fn(); + const deleteSnapshot = jest.fn(); + + const renderFlyout = ({ + snapshotState, + mlUpgradeModeEnabled, + }: { + snapshotState: SnapshotState; + mlUpgradeModeEnabled: boolean; + }) => + renderWithI18n( + + ); + + beforeEach(() => { + closeFlyout.mockClear(); + upgradeSnapshot.mockClear(); + deleteSnapshot.mockClear(); + }); + + it('renders flyout details and actions', () => { + const snapshotState: SnapshotState = { + jobId: MOCK_JOB_ID, + snapshotId: MOCK_SNAPSHOT_ID, + status: 'idle', + error: undefined, + }; + + renderFlyout({ snapshotState, mlUpgradeModeEnabled: false }); + + expect(screen.getByTestId('flyoutTitle')).toHaveTextContent('Upgrade or delete model snapshot'); + expect(screen.getByTestId('documentationLink')).toHaveAttribute('href', mockMlDeprecation.url); + expect(screen.getByTestId('upgradeSnapshotButton')).toHaveTextContent('Upgrade'); + expect(screen.getByTestId('deleteSnapshotButton')).toHaveTextContent('Delete'); + }); + + it('shows upgrade mode enabled callout and hides actions', () => { + const snapshotState: SnapshotState = { + jobId: MOCK_JOB_ID, + snapshotId: MOCK_SNAPSHOT_ID, + status: 'idle', + error: undefined, + }; + + renderFlyout({ snapshotState, mlUpgradeModeEnabled: true }); + + expect(screen.getByTestId('mlUpgradeModeEnabledError')).toBeInTheDocument(); + expect(screen.getByTestId('setUpgradeModeDocsLink')).toHaveAttribute( + 'href', + 'https://example.invalid/ml-upgrade-mode' + ); + expect(screen.queryByTestId('upgradeSnapshotButton')).toBeNull(); + expect(screen.queryByTestId('deleteSnapshotButton')).toBeNull(); + }); + + it('shows error callout and changes action labels on upgrade failure', () => { + const error: ResponseError = { + statusCode: 500, + message: 'Upgrade snapshot error', + }; + const snapshotState: SnapshotState = { + jobId: MOCK_JOB_ID, + snapshotId: MOCK_SNAPSHOT_ID, + status: 'error', + action: 'upgrade', + error, + }; + + renderFlyout({ snapshotState, mlUpgradeModeEnabled: false }); + + expect(screen.getByTestId('resolveSnapshotError')).toHaveTextContent( + 'Error upgrading snapshot' + ); + expect(screen.getByTestId('resolveSnapshotError')).toHaveTextContent('Upgrade snapshot error'); + expect(screen.getByTestId('upgradeSnapshotButton')).toHaveTextContent('Retry upgrade'); + }); + + it('calls upgradeSnapshot and closes flyout', () => { + const snapshotState: SnapshotState = { + jobId: MOCK_JOB_ID, + snapshotId: MOCK_SNAPSHOT_ID, + status: 'idle', + error: undefined, + }; + + renderFlyout({ snapshotState, mlUpgradeModeEnabled: false }); + + fireEvent.click(screen.getByTestId('upgradeSnapshotButton')); + expect(upgradeSnapshot).toHaveBeenCalled(); + expect(closeFlyout).toHaveBeenCalled(); + }); + + it('calls deleteSnapshot and closes flyout', () => { + const snapshotState: SnapshotState = { + jobId: MOCK_JOB_ID, + snapshotId: MOCK_SNAPSHOT_ID, + status: 'idle', + error: undefined, + }; + + renderFlyout({ snapshotState, mlUpgradeModeEnabled: false }); + + fireEvent.click(screen.getByTestId('deleteSnapshotButton')); + expect(deleteSnapshot).toHaveBeenCalled(); + expect(closeFlyout).toHaveBeenCalled(); + }); +}); From c078245d9cf2af30d7c6236d682856b830bbfbef Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Tue, 17 Mar 2026 15:31:57 +0100 Subject: [PATCH 3/9] [Upgrade Assistant] Add indices action/recommendation unit coverage ## Summary Adds focused unit tests for indices resolution/action UI logic (recommended actions and action-button visibility), and tightens the existing warning modal unit test to keep it assertion-free. ## Test plan - [x] node scripts/check_changes.ts - [x] yarn test:jest x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations - [x] yarn test:type_check --project x-pack/platform/plugins/private/upgrade_assistant/tsconfig.json Made-with: Cursor --- .../indices/actions_table_cell.test.tsx | 332 ++++++++++++++++++ .../steps/warning/warning_step_modal.test.tsx | 54 ++- .../indices/resolution_table_cell.test.tsx | 234 ++++++++++++ 3 files changed, 602 insertions(+), 18 deletions(-) create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx new file mode 100644 index 0000000000000..ead98c5f4d299 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx @@ -0,0 +1,332 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { ReindexStatus } from '@kbn/upgrade-assistant-pkg-common'; + +import type { + EnrichedDeprecationInfo, + ReindexAction, + UnfreezeAction, +} from '../../../../../../common/types'; +import type { IndexStateContext } from './context'; +import type { ReindexState } from './use_reindex'; +import type { UpdateIndexState } from './use_update_index'; +import { ReindexActionCell } from './actions_table_cell'; +import { LoadingState } from '../../../types'; + +const mockUseIndexContext = jest.fn(); + +jest.mock('./context', () => ({ + useIndexContext: () => mockUseIndexContext(), +})); + +const createReindexState = (overrides?: Partial): ReindexState => ({ + loadingState: LoadingState.Success, + errorMessage: null, + reindexTaskPercComplete: null, + status: undefined, + lastCompletedStep: undefined, + cancelLoadingState: undefined, + reindexWarnings: undefined, + hasRequiredPrivileges: true, + meta: { + indexName: 'test-index', + reindexName: 'test-index-reindexed', + aliases: [], + isFrozen: false, + isReadonly: false, + isInDataStream: false, + isClosedIndex: false, + isFollowerIndex: false, + }, + ...overrides, +}); + +const createUpdateIndexState = (overrides?: Partial): UpdateIndexState => ({ + failedBefore: false, + status: 'incomplete', + ...overrides, +}); + +const createIndexContext = ({ + deprecation, + reindexState, + updateIndexState, +}: { + deprecation: EnrichedDeprecationInfo; + reindexState: ReindexState; + updateIndexState: UpdateIndexState; +}): IndexStateContext => ({ + deprecation, + reindexState, + updateIndexState, + startReindex: jest.fn, []>(), + cancelReindex: jest.fn, []>(), + updateIndex: jest.fn, []>(), +}); + +const baseDeprecation = { + level: 'critical', + resolveDuringUpgrade: false, + type: 'index_settings', + message: 'Index created before 7.0', + details: 'deprecation details', + url: 'doc_url', + index: 'test-index', +} satisfies Omit; + +const reindexDeprecation = { + ...baseDeprecation, + correctiveAction: { + type: 'reindex', + metadata: { + isClosedIndex: false, + isFrozenIndex: false, + isInDataStream: false, + }, + } satisfies ReindexAction, +} satisfies EnrichedDeprecationInfo; + +const unfreezeDeprecation = { + ...baseDeprecation, + correctiveAction: { + type: 'unfreeze', + metadata: { + isClosedIndex: false, + isFrozenIndex: true, + isInDataStream: false, + }, + } satisfies UnfreezeAction, +} satisfies EnrichedDeprecationInfo; + +describe('ReindexActionCell', () => { + const mockOpenFlyout = jest.fn(); + const mockOpenModal = jest.fn(); + const mockSetSelectedResolutionType = jest.fn(); + + beforeEach(() => { + mockUseIndexContext.mockReset(); + mockOpenFlyout.mockClear(); + mockOpenModal.mockClear(); + mockSetSelectedResolutionType.mockClear(); + }); + + it('displays reindex and unfreeze actions for frozen indices', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: unfreezeDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n( + + ); + + expect(screen.getByTestId('deprecation-unfreeze-reindex')).toBeInTheDocument(); + expect(screen.getByTestId('deprecation-unfreeze-unfreeze')).toBeInTheDocument(); + }); + + it('only displays reindex action if reindex is in progress (frozen indices)', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: unfreezeDeprecation, + reindexState: createReindexState({ status: ReindexStatus.inProgress }), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n( + + ); + + expect(screen.getByTestId('deprecation-unfreeze-reindex')).toBeInTheDocument(); + expect(screen.queryByTestId('deprecation-unfreeze-unfreeze')).toBeNull(); + }); + + it('only displays unfreeze action if unfreezing is in progress (frozen indices)', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: unfreezeDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState({ status: 'inProgress' }), + }) + ); + + renderWithI18n( + + ); + + expect(screen.queryByTestId('deprecation-unfreeze-reindex')).toBeNull(); + expect(screen.getByTestId('deprecation-unfreeze-unfreeze')).toBeInTheDocument(); + }); + + it('displays reindex and read-only actions when both are valid', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n( + + ); + + expect(screen.getByTestId('deprecation-reindex-reindex')).toBeInTheDocument(); + expect(screen.getByTestId('deprecation-reindex-readonly')).toBeInTheDocument(); + }); + + it('only displays read-only action if reindexing is excluded', () => { + const excludedReindexDeprecation: EnrichedDeprecationInfo = { + ...reindexDeprecation, + correctiveAction: { + ...(reindexDeprecation.correctiveAction as ReindexAction), + excludedActions: ['reindex'], + }, + }; + + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: excludedReindexDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n( + + ); + + expect(screen.queryByTestId('deprecation-reindex-reindex')).toBeNull(); + expect(screen.getByTestId('deprecation-reindex-readonly')).toBeInTheDocument(); + }); + + it('only displays read-only action if index is a follower index', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState({ + meta: { ...createReindexState().meta, isFollowerIndex: true }, + }), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n( + + ); + + expect(screen.queryByTestId('deprecation-reindex-reindex')).toBeNull(); + expect(screen.getByTestId('deprecation-reindex-readonly')).toBeInTheDocument(); + }); + + it('only displays reindex action if read-only is excluded', () => { + const excludedReadOnlyDeprecation: EnrichedDeprecationInfo = { + ...reindexDeprecation, + correctiveAction: { + ...(reindexDeprecation.correctiveAction as ReindexAction), + excludedActions: ['readOnly'], + }, + }; + + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: excludedReadOnlyDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n( + + ); + + expect(screen.getByTestId('deprecation-reindex-reindex')).toBeInTheDocument(); + expect(screen.queryByTestId('deprecation-reindex-readonly')).toBeNull(); + }); + + it('only displays read-only action when readonly update is in progress', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState({ status: 'inProgress' }), + }) + ); + + renderWithI18n( + + ); + + expect(screen.queryByTestId('deprecation-reindex-reindex')).toBeNull(); + expect(screen.getByTestId('deprecation-reindex-readonly')).toBeInTheDocument(); + }); + + it('calls open handlers and sets resolution type on click', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n( + + ); + + fireEvent.click(screen.getByTestId('deprecation-reindex-reindex')); + expect(mockOpenFlyout).toHaveBeenCalledTimes(1); + + fireEvent.click(screen.getByTestId('deprecation-reindex-readonly')); + expect(mockOpenModal).toHaveBeenCalledTimes(1); + expect(mockSetSelectedResolutionType).toHaveBeenCalledWith('readonly'); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx index 0c86348835909..898fc359b4833 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx @@ -9,8 +9,9 @@ import React from 'react'; import { screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom'; import type { EnrichedDeprecationInfo } from '../../../../../../../../../common/types'; -import type { IndexWarning, IndexWarningType } from '@kbn/reindex-service-plugin/common'; +import type { IndexWarning } from '@kbn/reindex-service-plugin/common'; import { ReindexStatus } from '@kbn/upgrade-assistant-pkg-common'; +import { LoadingState } from '../../../../../../types'; import { renderWithI18n } from '@kbn/test-jest-helpers'; import { WarningModalStep } from './warning_step_modal'; @@ -29,22 +30,40 @@ jest.mock('../../../../../../../app_context', () => ({ })); jest.mock('./warning_step_checkbox', () => ({ - DeprecatedSettingWarningCheckbox: ({ isChecked, onChange, id }: any) => ( - - ), - ReplaceIndexWithAliasWarningCheckbox: ({ isChecked, onChange, id }: any) => ( - - ), - MakeIndexReadonlyWarningCheckbox: ({ isChecked, onChange, id }: any) => ( - - ), + DeprecatedSettingWarningCheckbox: ({ + isChecked, + onChange, + id, + }: { + isChecked: boolean; + onChange: (event: React.ChangeEvent) => void; + id: string; + }) => , + ReplaceIndexWithAliasWarningCheckbox: ({ + isChecked, + onChange, + id, + }: { + isChecked: boolean; + onChange: (event: React.ChangeEvent) => void; + id: string; + }) => , + MakeIndexReadonlyWarningCheckbox: ({ + isChecked, + onChange, + id, + }: { + isChecked: boolean; + onChange: (event: React.ChangeEvent) => void; + id: string; + }) => , })); jest.mock('../callouts', () => ({ FollowerIndexCallout: () =>
, ESTransformsTargetCallout: () =>
, MlAnomalyCallout: () =>
, - FetchFailedCallOut: ({ errorMessage }: any) => ( + FetchFailedCallOut: ({ errorMessage }: { errorMessage: string }) => (
{errorMessage}
), })); @@ -53,9 +72,9 @@ jest.mock('../../../../../common/nodes_low_disk_space', () => ({ NodesLowSpaceCallOut: () =>
, })); -// Define a local mock ReindexState type for testing const mockReindexState = { status: ReindexStatus.inProgress, + loadingState: LoadingState.Success, meta: { indexName: 'test-index', reindexName: 'test-index-reindex', @@ -66,23 +85,22 @@ const mockReindexState = { isClosedIndex: false, isFollowerIndex: false, }, - errorMessage: '', - loadingState: 'idle' as any, // type assertion workaround for tests + errorMessage: null, reindexTaskPercComplete: null, // valid value for number | null }; const mockDeprecation: EnrichedDeprecationInfo = { - type: 'index' as any, + type: 'index_settings', level: 'warning', resolveDuringUpgrade: false, - url: '', + url: 'doc_url', message: 'Deprecation message', correctiveAction: undefined, }; const mockWarnings: IndexWarning[] = [ - { warningType: 'indexSetting' as IndexWarningType, meta: {}, flow: 'readonly' }, - { warningType: 'replaceIndexWithAlias' as IndexWarningType, meta: {}, flow: 'readonly' }, + { warningType: 'indexSetting', meta: {}, flow: 'readonly' }, + { warningType: 'replaceIndexWithAlias', meta: {}, flow: 'readonly' }, ]; const baseProps = { diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx new file mode 100644 index 0000000000000..6695ca87115db --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx @@ -0,0 +1,234 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import { ReindexStatus } from '@kbn/upgrade-assistant-pkg-common'; + +import type { + EnrichedDeprecationInfo, + ReindexAction, + UnfreezeAction, +} from '../../../../../../common/types'; +import type { IndexStateContext } from './context'; +import type { ReindexState } from './use_reindex'; +import type { UpdateIndexState } from './use_update_index'; +import { ReindexResolutionCell } from './resolution_table_cell'; +import { LoadingState } from '../../../types'; + +const mockUseIndexContext = jest.fn(); + +jest.mock('./context', () => ({ + useIndexContext: () => mockUseIndexContext(), +})); + +const createReindexState = (overrides?: Partial): ReindexState => ({ + loadingState: LoadingState.Success, + errorMessage: null, + reindexTaskPercComplete: null, + status: undefined, + lastCompletedStep: undefined, + cancelLoadingState: undefined, + reindexWarnings: undefined, + hasRequiredPrivileges: true, + meta: { + indexName: 'test-index', + reindexName: 'test-index-reindexed', + aliases: [], + isFrozen: false, + isReadonly: false, + isInDataStream: false, + isClosedIndex: false, + isFollowerIndex: false, + }, + ...overrides, +}); + +const createUpdateIndexState = (overrides?: Partial): UpdateIndexState => ({ + failedBefore: false, + status: 'incomplete', + ...overrides, +}); + +const createIndexContext = ({ + deprecation, + reindexState, + updateIndexState, +}: { + deprecation: EnrichedDeprecationInfo; + reindexState: ReindexState; + updateIndexState: UpdateIndexState; +}): IndexStateContext => ({ + deprecation, + reindexState, + updateIndexState, + startReindex: jest.fn, []>(), + cancelReindex: jest.fn, []>(), + updateIndex: jest.fn, []>(), +}); + +const baseDeprecation = { + level: 'critical', + resolveDuringUpgrade: false, + type: 'index_settings', + message: 'Index created before 7.0', + details: 'deprecation details', + url: 'doc_url', + index: 'test-index', +} satisfies Omit; + +const reindexDeprecation = { + ...baseDeprecation, + correctiveAction: { + type: 'reindex', + metadata: { + isClosedIndex: false, + isFrozenIndex: false, + isInDataStream: false, + }, + } satisfies ReindexAction, +} satisfies EnrichedDeprecationInfo; + +const unfreezeDeprecation = { + ...baseDeprecation, + correctiveAction: { + type: 'unfreeze', + metadata: { + isClosedIndex: false, + isFrozenIndex: true, + isInDataStream: false, + }, + } satisfies UnfreezeAction, +} satisfies EnrichedDeprecationInfo; + +describe('ReindexResolutionCell', () => { + beforeEach(() => { + mockUseIndexContext.mockReset(); + }); + + it('recommends unfreeze for frozen indices', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: unfreezeDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Recommended: unfreeze')).toBeInTheDocument(); + }); + + it('recommends set to read-only for follower indices', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState({ + meta: { ...createReindexState().meta, isFollowerIndex: true }, + }), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Recommended: set to read-only')).toBeInTheDocument(); + }); + + it('recommends set to read-only for large indices', () => { + const largeIndexDeprecation: EnrichedDeprecationInfo = { + ...reindexDeprecation, + correctiveAction: { + ...(reindexDeprecation.correctiveAction as ReindexAction), + indexSizeInBytes: 1073741825, + }, + }; + + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: largeIndexDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Recommended: set to read-only')).toBeInTheDocument(); + }); + + it('recommends reindexing if index is already read-only', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState({ + meta: { ...createReindexState().meta, isReadonly: true }, + }), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Recommended: reindex')).toBeInTheDocument(); + }); + + it('recommends set to read-only if reindexing is excluded', () => { + const excludedReindexDeprecation: EnrichedDeprecationInfo = { + ...reindexDeprecation, + correctiveAction: { + ...(reindexDeprecation.correctiveAction as ReindexAction), + excludedActions: ['reindex'], + }, + }; + + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: excludedReindexDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Recommended: set to read-only')).toBeInTheDocument(); + }); + + it('recommends manual fix if follower index is already read-only', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState({ + meta: { ...createReindexState().meta, isFollowerIndex: true, isReadonly: true }, + }), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Resolve manually')).toBeInTheDocument(); + }); + + it('shows success state when readonly update completed after reindex failure', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState({ status: ReindexStatus.failed }), + updateIndexState: createUpdateIndexState({ status: 'complete' }), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Index is set to read-only')).toBeInTheDocument(); + }); +}); From 6ad3d141080157e71c2f973d3885283c2fe3bf50 Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Tue, 17 Mar 2026 15:32:13 +0100 Subject: [PATCH 4/9] [Upgrade Assistant] Add data streams migration unit coverage ## Summary Adds unit tests for data stream migration UI behaviors, including recommended action and action-button visibility, container metadata formatting, confirm steps (reindex/readonly) and checklist/progress states. ## Test plan - [x] node scripts/check_changes.ts - [x] yarn test:jest x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations - [x] yarn test:type_check --project x-pack/platform/plugins/private/upgrade_assistant/tsconfig.json Made-with: Cursor --- .../data_streams/actions_table_cell.test.tsx | 201 ++++++++++++++++++ .../data_streams/flyout/container.test.tsx | 135 ++++++++++++ .../checklist/checklist_reindex_step.test.tsx | 113 ++++++++++ .../flyout/steps/checklist/progress.test.tsx | 77 +++++++ .../confirm/readonly_confirm_step.test.tsx | 162 ++++++++++++++ .../confirm/reindex_confirm_step.test.tsx | 159 ++++++++++++++ .../resolution_table_cell.test.tsx | 93 ++++++++ 7 files changed, 940 insertions(+) create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx new file mode 100644 index 0000000000000..fb010ee7f83f0 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { DataStreamsAction } from '../../../../../../common/types'; +import { DataStreamMigrationStatus } from '../../../../../../common/types'; +import type { MigrationStateContext } from './context'; +import { DataStreamReindexActionsCell } from './actions_table_cell'; +import { LoadingState } from '../../../types'; + +const mockUseDataStreamMigrationContext = jest.fn(); + +jest.mock('./context', () => ({ + useDataStreamMigrationContext: () => mockUseDataStreamMigrationContext(), +})); + +const baseCorrectiveAction: DataStreamsAction = { + type: 'dataStream', + metadata: { + totalBackingIndices: 2, + indicesRequiringUpgradeCount: 1, + indicesRequiringUpgrade: ['.ds-my-ds-000001'], + ignoredIndicesRequiringUpgrade: [], + ignoredIndicesRequiringUpgradeCount: 0, + reindexRequired: true, + }, +}; + +const createContext = ({ + status, + resolutionType, + excludedActions, +}: { + status: DataStreamMigrationStatus; + resolutionType?: 'reindex' | 'readonly'; + excludedActions?: Array<'readOnly' | 'reindex'>; +}): MigrationStateContext => ({ + loadDataStreamMetadata: jest.fn, []>(), + initMigration: jest.fn(), + startReindex: jest.fn, []>(), + cancelReindex: jest.fn, []>(), + startReadonly: jest.fn, []>(), + cancelReadonly: jest.fn, []>(), + migrationState: { + loadingState: LoadingState.Success, + status, + resolutionType, + taskPercComplete: null, + errorMessage: null, + meta: null, + hasRequiredPrivileges: true, + }, +}); + +describe('DataStreamReindexActionsCell', () => { + const mockOpenFlyout = jest.fn(); + const mockOpenModal = jest.fn(); + + beforeEach(() => { + mockUseDataStreamMigrationContext.mockReset(); + mockOpenFlyout.mockClear(); + mockOpenModal.mockClear(); + }); + + it('displays both read-only and reindex actions when both are valid', () => { + mockUseDataStreamMigrationContext.mockReturnValue( + createContext({ + status: DataStreamMigrationStatus.notStarted, + }) + ); + + renderWithI18n( + + ); + + expect(screen.getByTestId('deprecation-dataStream-reindex')).toBeInTheDocument(); + expect(screen.getByTestId('deprecation-dataStream-readonly')).toBeInTheDocument(); + }); + + it('only displays reindex action if read-only is excluded', () => { + const correctiveAction: DataStreamsAction = { + ...baseCorrectiveAction, + metadata: { ...baseCorrectiveAction.metadata, excludedActions: ['readOnly'] }, + }; + + mockUseDataStreamMigrationContext.mockReturnValue( + createContext({ + status: DataStreamMigrationStatus.notStarted, + }) + ); + + renderWithI18n( + + ); + + expect(screen.getByTestId('deprecation-dataStream-reindex')).toBeInTheDocument(); + expect(screen.queryByTestId('deprecation-dataStream-readonly')).toBeNull(); + }); + + it('only displays read-only action if reindex is excluded', () => { + const correctiveAction: DataStreamsAction = { + ...baseCorrectiveAction, + metadata: { ...baseCorrectiveAction.metadata, excludedActions: ['reindex'] }, + }; + + mockUseDataStreamMigrationContext.mockReturnValue( + createContext({ + status: DataStreamMigrationStatus.notStarted, + }) + ); + + renderWithI18n( + + ); + + expect(screen.queryByTestId('deprecation-dataStream-reindex')).toBeNull(); + expect(screen.getByTestId('deprecation-dataStream-readonly')).toBeInTheDocument(); + }); + + it('only displays reindex action when reindexing is in progress', () => { + mockUseDataStreamMigrationContext.mockReturnValue( + createContext({ + status: DataStreamMigrationStatus.inProgress, + resolutionType: 'reindex', + }) + ); + + renderWithI18n( + + ); + + expect(screen.getByTestId('deprecation-dataStream-reindex')).toBeInTheDocument(); + expect(screen.queryByTestId('deprecation-dataStream-readonly')).toBeNull(); + }); + + it('only displays read-only action when setting read-only is in progress', () => { + mockUseDataStreamMigrationContext.mockReturnValue( + createContext({ + status: DataStreamMigrationStatus.inProgress, + resolutionType: 'readonly', + }) + ); + + renderWithI18n( + + ); + + expect(screen.queryByTestId('deprecation-dataStream-reindex')).toBeNull(); + expect(screen.getByTestId('deprecation-dataStream-readonly')).toBeInTheDocument(); + }); + + it('calls open handlers on click', () => { + mockUseDataStreamMigrationContext.mockReturnValue( + createContext({ + status: DataStreamMigrationStatus.notStarted, + }) + ); + + renderWithI18n( + + ); + + fireEvent.click(screen.getByTestId('deprecation-dataStream-reindex')); + expect(mockOpenFlyout).toHaveBeenCalledTimes(1); + + fireEvent.click(screen.getByTestId('deprecation-dataStream-readonly')); + expect(mockOpenModal).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx new file mode 100644 index 0000000000000..782138402ed7a --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; +import moment from 'moment'; +import numeral from '@elastic/numeral'; + +import type { EnrichedDeprecationInfo } from '../../../../../../../common/types'; +import { DataStreamMigrationStatus } from '../../../../../../../common/types'; +import { LoadingState } from '../../../../types'; +import type { MigrationState } from '../use_migration_state'; +import { DataStreamReindexFlyout } from './container'; + +jest.mock('../../../../../app_context', () => ({ + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => ({ data: [] }), + }, + core: { + docLinks: { links: { upgradeAssistant: { dataStreamReindex: 'https://example.invalid' } } }, + }, + }, + }), +})); + +jest.mock('../use_migration_step', () => ({ + useMigrationStep: () => ['confirm', jest.fn()] as const, +})); + +jest.mock('./steps/confirm', () => ({ + ConfirmMigrationReindexFlyoutStep: ({ + lastIndexCreationDateFormatted, + }: { + lastIndexCreationDateFormatted: string; + }) =>
{lastIndexCreationDateFormatted}
, + ConfirmMigrationReadonlyFlyoutStep: () =>
, +})); + +jest.mock('./steps/checklist', () => ({ + ChecklistFlyoutStep: () =>
, +})); + +jest.mock('./steps/completed', () => ({ + MigrationCompletedFlyoutStep: () =>
, +})); + +jest.mock('../../../common/initializing_step', () => ({ + InitializingStep: () =>
, +})); + +const DATE_FORMAT = 'dddd, MMMM Do YYYY, h:mm:ss a'; +const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; + +const mockDeprecation: EnrichedDeprecationInfo = { + type: 'data_streams', + level: 'warning', + resolveDuringUpgrade: false, + url: 'doc_url', + message: 'Data stream needs to be reindexed or set to read-only', + index: 'my-data-stream', + correctiveAction: { + type: 'dataStream', + metadata: { + totalBackingIndices: 2, + indicesRequiringUpgradeCount: 1, + indicesRequiringUpgrade: ['.ds-my-data-stream-000001'], + ignoredIndicesRequiringUpgrade: [], + ignoredIndicesRequiringUpgradeCount: 0, + reindexRequired: true, + }, + }, +}; + +const createMigrationState = (): MigrationState => ({ + loadingState: LoadingState.Success, + status: DataStreamMigrationStatus.notStarted, + taskPercComplete: null, + errorMessage: null, + resolutionType: 'reindex', + meta: { + dataStreamName: 'my-data-stream', + documentationUrl: 'https://example.invalid/docs', + lastIndexRequiringUpgradeCreationDate: 1700000000000, + indicesRequiringUpgradeCount: 1, + indicesRequiringUpgrade: ['.ds-my-data-stream-000001'], + allIndices: ['.ds-my-data-stream-000001', '.ds-my-data-stream-000002'], + allIndicesCount: 2, + indicesRequiringUpgradeDocsCount: 10, + indicesRequiringUpgradeDocsSize: 1024, + }, +}); + +describe('DataStreamReindexFlyout', () => { + it('renders formatted metadata in the flyout header', () => { + const migrationState = createMigrationState(); + const expectedLastIndexCreationDateFormatted = moment( + migrationState.meta?.lastIndexRequiringUpgradeCreationDate + ).format(DATE_FORMAT); + const expectedDocsSizeFormatted = numeral( + migrationState.meta?.indicesRequiringUpgradeDocsSize + ).format(FILE_SIZE_DISPLAY_FORMAT); + + renderWithI18n( + , []>()} + migrationState={migrationState} + initMigration={jest.fn()} + startReindex={jest.fn, []>()} + cancelReindex={jest.fn, []>()} + startReadonly={jest.fn, []>()} + cancelReadonly={jest.fn, []>()} + /> + ); + + expect(screen.getByTestId('flyoutTitle')).toHaveTextContent('my-data-stream'); + expect(screen.getByTestId('dataStreamLastIndexCreationDate')).toHaveTextContent( + expectedLastIndexCreationDateFormatted + ); + expect(screen.getByTestId('dataStreamSize')).toHaveTextContent(expectedDocsSizeFormatted); + expect(screen.getByTestId('dataStreamDocumentCount')).toHaveTextContent('10'); + expect(screen.getByTestId('confirmMigrationStep')).toHaveTextContent( + expectedLastIndexCreationDateFormatted + ); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx new file mode 100644 index 0000000000000..68ec094025bbd --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import { DataStreamMigrationStatus } from '../../../../../../../../../common/types'; +import { LoadingState } from '../../../../../../types'; +import type { MigrationState } from '../../../use_migration_state'; +import { ChecklistFlyoutStep } from './checklist_reindex_step'; + +jest.mock('../../../../../../../app_context', () => ({ + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => ({ data: [] }), + }, + }, + }), +})); + +jest.mock('../../../../../common/nodes_low_disk_space', () => ({ + NodesLowSpaceCallOut: () =>
, +})); + +jest.mock('./callouts', () => ({ + FetchFailedCallout: ({ hasFetchFailed }: { hasFetchFailed: boolean }) => ( +
{String(hasFetchFailed)}
+ ), + NoPrivilegesCallout: () =>
, +})); + +jest.mock('./progress', () => ({ + MigrationProgress: () =>
, +})); + +const createMigrationState = (overrides: Partial): MigrationState => ({ + loadingState: LoadingState.Success, + status: DataStreamMigrationStatus.notStarted, + taskPercComplete: null, + errorMessage: null, + meta: null, + hasRequiredPrivileges: true, + resolutionType: 'reindex', + taskStatus: undefined, + cancelLoadingState: undefined, + migrationWarnings: undefined, + ...overrides, +}); + +describe('ChecklistFlyoutStep (data streams)', () => { + it('renders fetch-failed callout and hides main action button on fetchFailed', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('dataStreamMigrationChecklistFlyout')).toBeInTheDocument(); + expect(screen.getByTestId('fetchFailedCallout')).toHaveTextContent('true'); + expect(screen.queryByTestId('startDataStreamMigrationButton')).toBeNull(); + }); + + it('shows try-again button on failed status', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('fetchFailedCallout')).toHaveTextContent('false'); + expect(screen.getByTestId('startDataStreamMigrationButton')).toHaveTextContent('Try again'); + }); + + it('shows cancel button when migration is in progress', () => { + const cancelAction = jest.fn(); + + renderWithI18n( + + ); + + fireEvent.click(screen.getByTestId('cancelDataStreamMigrationButton')); + expect(cancelAction).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx new file mode 100644 index 0000000000000..aea668b2b4cd5 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { DataStreamProgressDetails } from '../../../../../../../../../common/types'; +import { DataStreamMigrationStatus } from '../../../../../../../../../common/types'; +import { LoadingState } from '../../../../../../types'; +import type { MigrationState } from '../../../use_migration_state'; +import { MigrationProgress } from './progress'; + +jest.mock('./progress_title', () => ({ + MigrateDocumentsStepTitle: () => , +})); + +describe('MigrationProgress', () => { + it('renders the per-status counts for reindex resolution type', () => { + const taskStatus: DataStreamProgressDetails = { + startTimeMs: 1700000000000, + successCount: 2, + errorsCount: 1, + inProgressCount: 3, + pendingCount: 4, + }; + + const migrationState: MigrationState = { + loadingState: LoadingState.Success, + status: DataStreamMigrationStatus.inProgress, + taskPercComplete: 50, + errorMessage: null, + resolutionType: 'reindex', + meta: null, + taskStatus, + }; + + renderWithI18n(); + + expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent('logs-foo'); + expect(screen.getByText('1 Index failed to get reindexed.')).toBeInTheDocument(); + expect(screen.getByText('2 Indices successfully reindexed.')).toBeInTheDocument(); + expect(screen.getByText('3 Indices currently getting reindexed.')).toBeInTheDocument(); + expect(screen.getByText('4 Indices waiting to start.')).toBeInTheDocument(); + }); + + it('renders the per-status counts for readonly resolution type', () => { + const taskStatus: DataStreamProgressDetails = { + startTimeMs: 1700000000000, + successCount: 1, + errorsCount: 0, + inProgressCount: 0, + pendingCount: 1, + }; + + const migrationState: MigrationState = { + loadingState: LoadingState.Success, + status: DataStreamMigrationStatus.inProgress, + taskPercComplete: 20, + errorMessage: null, + resolutionType: 'readonly', + meta: null, + taskStatus, + }; + + renderWithI18n(); + + expect(screen.getByText('1 Index successfully set to read-only.')).toBeInTheDocument(); + expect(screen.getByText('0 Indices currently getting set to read-only.')).toBeInTheDocument(); + expect(screen.getByText('1 Index waiting to start.')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx new file mode 100644 index 0000000000000..0de2c35a63d60 --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { + DataStreamMigrationWarning, + DataStreamMetadata, +} from '../../../../../../../../../common/types'; +import type { WarningCheckboxProps } from './warnings'; +import { ConfirmMigrationReadonlyFlyoutStep } from './readonly_confirm_step'; + +const mockLoadNodeDiskSpace = jest.fn< + { + data: Array<{ + nodeName: string; + availableBytes: number; + lowDiskSpace: boolean; + shards: string[]; + }>; + }, + [] +>(); + +jest.mock('../../../../../../../app_context', () => ({ + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => mockLoadNodeDiskSpace(), + }, + core: { + docLinks: { + links: { + upgradeAssistant: { + dataStreamReindex: 'https://example.invalid/reindex-docs', + }, + }, + }, + }, + }, + }), +})); + +jest.mock('./warnings', () => ({ + IncompatibleDataInDataStreamWarningCheckbox: ({ + isChecked, + onChange, + id, + }: WarningCheckboxProps) => ( + + ), + AffectExistingSetupsWarningCheckbox: ({ isChecked, onChange, id }: WarningCheckboxProps) => ( + + ), +})); + +jest.mock('../../../../../common/nodes_low_disk_space', () => ({ + NodesLowSpaceCallOut: () =>
, +})); + +const mockMeta: DataStreamMetadata = { + dataStreamName: 'my-data-stream', + documentationUrl: 'https://example.invalid/meta-docs', + lastIndexRequiringUpgradeCreationDate: 1700000000000, + allIndices: ['.ds-my-data-stream-000001', '.ds-my-data-stream-000002'], + allIndicesCount: 2, + indicesRequiringUpgradeCount: 1, + indicesRequiringUpgrade: ['.ds-my-data-stream-000001'], + indicesRequiringUpgradeDocsCount: 10, + indicesRequiringUpgradeDocsSize: 1024, +}; + +const mockWarnings: DataStreamMigrationWarning[] = [ + { warningType: 'incompatibleDataStream', meta: {}, resolutionType: 'readonly' }, + { warningType: 'affectExistingSetups', meta: {}, resolutionType: 'readonly' }, +]; + +describe('ConfirmMigrationReadonlyFlyoutStep', () => { + beforeEach(() => { + mockLoadNodeDiskSpace.mockReset(); + mockLoadNodeDiskSpace.mockReturnValue({ data: [] }); + }); + + it('blocks start until all warning checkboxes are checked', () => { + const startAction = jest.fn(); + + const { container } = renderWithI18n( + + ); + + expect(screen.getByTestId('readonlyDataStreamModalTitle')).toHaveTextContent( + 'Set data stream to read-only' + ); + expect(screen.getByTestId('dataStreamMigrationWarningsCallout')).toBeInTheDocument(); + expect(screen.getByTestId('readOnlyDsWarningCallout')).toBeInTheDocument(); + + const startButton = screen.getByTestId('startActionButton'); + expect(startButton).toBeDisabled(); + + const checkboxes = container.querySelectorAll( + 'input[type="checkbox"][data-testid^="migrationWarning-"]' + ); + expect(checkboxes.length).toBe(2); + checkboxes.forEach((checkbox) => fireEvent.click(checkbox)); + + expect(startButton).not.toBeDisabled(); + fireEvent.click(startButton); + expect(startAction).toHaveBeenCalled(); + }); + + it('renders nodes low disk space callout when nodes are present', () => { + mockLoadNodeDiskSpace.mockReturnValue({ + data: [ + { + nodeName: 'node-1', + availableBytes: 1024, + lowDiskSpace: true, + shards: ['shard-1'], + }, + ], + }); + + renderWithI18n( + + ); + + expect(screen.getByTestId('nodesLowDiskSpaceCallout')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx new file mode 100644 index 0000000000000..5317647fec65b --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { fireEvent, screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { + DataStreamMigrationWarning, + DataStreamMetadata, +} from '../../../../../../../../../common/types'; +import type { WarningCheckboxProps } from './warnings'; +import { ConfirmMigrationReindexFlyoutStep } from './reindex_confirm_step'; + +const mockLoadNodeDiskSpace = jest.fn< + { + data: Array<{ + nodeName: string; + availableBytes: number; + lowDiskSpace: boolean; + shards: string[]; + }>; + }, + [] +>(); + +jest.mock('../../../../../../../app_context', () => ({ + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => mockLoadNodeDiskSpace(), + }, + core: { + docLinks: { + links: { + upgradeAssistant: { + dataStreamReindex: 'https://example.invalid/reindex-docs', + }, + }, + }, + }, + }, + }), +})); + +jest.mock('./warnings', () => ({ + IncompatibleDataInDataStreamWarningCheckbox: ({ + isChecked, + onChange, + id, + }: WarningCheckboxProps) => ( + + ), + AffectExistingSetupsWarningCheckbox: ({ isChecked, onChange, id }: WarningCheckboxProps) => ( + + ), +})); + +jest.mock('../../../../../common/nodes_low_disk_space', () => ({ + NodesLowSpaceCallOut: () =>
, +})); + +const mockMeta: DataStreamMetadata = { + dataStreamName: 'my-data-stream', + documentationUrl: 'https://example.invalid/meta-docs', + lastIndexRequiringUpgradeCreationDate: 1700000000000, + allIndices: ['.ds-my-data-stream-000001', '.ds-my-data-stream-000002'], + allIndicesCount: 2, + indicesRequiringUpgradeCount: 1, + indicesRequiringUpgrade: ['.ds-my-data-stream-000001'], + indicesRequiringUpgradeDocsCount: 10, + indicesRequiringUpgradeDocsSize: 1024, +}; + +const mockWarnings: DataStreamMigrationWarning[] = [ + { warningType: 'incompatibleDataStream', meta: {}, resolutionType: 'reindex' }, + { warningType: 'affectExistingSetups', meta: {}, resolutionType: 'reindex' }, +]; + +describe('ConfirmMigrationReindexFlyoutStep', () => { + beforeEach(() => { + mockLoadNodeDiskSpace.mockReset(); + mockLoadNodeDiskSpace.mockReturnValue({ data: [] }); + }); + + it('blocks start until all warning checkboxes are checked', () => { + const startAction = jest.fn(); + + const { container } = renderWithI18n( + + ); + + expect(screen.getByTestId('dataStreamMigrationWarningsCallout')).toBeInTheDocument(); + expect(screen.getByTestId('reindexDsWarningCallout')).toBeInTheDocument(); + + const startButton = screen.getByTestId('startActionButton'); + expect(startButton).toBeDisabled(); + + const checkboxes = container.querySelectorAll( + 'input[type="checkbox"][data-testid^="migrationWarning-"]' + ); + expect(checkboxes.length).toBe(2); + checkboxes.forEach((checkbox) => fireEvent.click(checkbox)); + + expect(startButton).not.toBeDisabled(); + fireEvent.click(startButton); + expect(startAction).toHaveBeenCalled(); + }); + + it('renders nodes low disk space callout when nodes are present', () => { + mockLoadNodeDiskSpace.mockReturnValue({ + data: [ + { + nodeName: 'node-1', + availableBytes: 1024, + lowDiskSpace: true, + shards: ['shard-1'], + }, + ], + }); + + renderWithI18n( + + ); + + expect(screen.getByTestId('nodesLowDiskSpaceCallout')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx new file mode 100644 index 0000000000000..213e3ee5a6d4d --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import '@testing-library/jest-dom'; +import { screen } from '@testing-library/react'; +import { renderWithI18n } from '@kbn/test-jest-helpers'; + +import type { DataStreamsAction } from '../../../../../../common/types'; +import { DataStreamMigrationStatus } from '../../../../../../common/types'; +import type { MigrationStateContext } from './context'; +import { DataStreamReindexResolutionCell } from './resolution_table_cell'; +import { LoadingState } from '../../../types'; + +const mockUseDataStreamMigrationContext = jest.fn(); + +jest.mock('./context', () => ({ + useDataStreamMigrationContext: () => mockUseDataStreamMigrationContext(), +})); + +const baseCorrectiveAction: DataStreamsAction = { + type: 'dataStream', + metadata: { + totalBackingIndices: 2, + indicesRequiringUpgradeCount: 1, + indicesRequiringUpgrade: ['.ds-my-ds-000001'], + ignoredIndicesRequiringUpgrade: [], + ignoredIndicesRequiringUpgradeCount: 0, + reindexRequired: true, + }, +}; + +describe('DataStreamReindexResolutionCell', () => { + beforeEach(() => { + mockUseDataStreamMigrationContext.mockReset(); + }); + + it('recommends set to read-only by default', () => { + mockUseDataStreamMigrationContext.mockReturnValue({ + loadDataStreamMetadata: jest.fn, []>(), + initMigration: jest.fn(), + startReindex: jest.fn, []>(), + cancelReindex: jest.fn, []>(), + startReadonly: jest.fn, []>(), + cancelReadonly: jest.fn, []>(), + migrationState: { + loadingState: LoadingState.Success, + status: DataStreamMigrationStatus.notStarted, + taskPercComplete: null, + errorMessage: null, + meta: null, + }, + }); + + renderWithI18n(); + + expect(screen.getByText('Recommended: set to read-only')).toBeInTheDocument(); + }); + + it('recommends reindex when read-only is excluded', () => { + const correctiveAction: DataStreamsAction = { + ...baseCorrectiveAction, + metadata: { + ...baseCorrectiveAction.metadata, + excludedActions: ['readOnly'], + }, + }; + + mockUseDataStreamMigrationContext.mockReturnValue({ + loadDataStreamMetadata: jest.fn, []>(), + initMigration: jest.fn(), + startReindex: jest.fn, []>(), + cancelReindex: jest.fn, []>(), + startReadonly: jest.fn, []>(), + cancelReadonly: jest.fn, []>(), + migrationState: { + loadingState: LoadingState.Success, + status: DataStreamMigrationStatus.notStarted, + taskPercComplete: null, + errorMessage: null, + meta: null, + }, + }); + + renderWithI18n(); + + expect(screen.getByText('Recommended: reindex')).toBeInTheDocument(); + }); +}); From 5cd1a5911a4ec20a49d9f2871221aa83c19b82c7 Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Tue, 17 Mar 2026 15:32:27 +0100 Subject: [PATCH 5/9] [Upgrade Assistant] Remove ES deprecations client integration tests ## Summary Removes flaky client integration coverage for ES deprecations now replaced by focused unit tests. ## Test plan - [x] node scripts/check_changes.ts - [x] yarn test:jest x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations - [x] yarn test:type_check --project x-pack/platform/plugins/private/upgrade_assistant/tsconfig.json Made-with: Cursor --- ...luster_settings_deprecation_flyout.test.ts | 141 --- .../data_streams_deprecation.test.ts | 599 ----------- .../default_deprecation_flyout.test.ts | 61 -- .../es_deprecations/deprecations_list.test.ts | 994 ------------------ .../es_deprecations/error_handling.test.ts | 95 -- .../es_deprecations.helpers.tsx | 86 -- .../index_settings_deprecation_flyout.test.ts | 152 --- .../ml_snapshots_deprecation_flyout.test.ts | 291 ----- .../es_deprecations/mocked_responses.ts | 210 ---- .../readonly_index_modal.test.ts | 153 --- .../reindex_deprecation_flyout.test.ts | 245 ----- 11 files changed, 3027 deletions(-) delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/cluster_settings_deprecation_flyout.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/data_streams_deprecation.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.tsx delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/readonly_index_modal.test.ts delete mode 100644 x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/cluster_settings_deprecation_flyout.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/cluster_settings_deprecation_flyout.test.ts deleted file mode 100644 index d74b9e2cfef07..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/cluster_settings_deprecation_flyout.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { fireEvent, screen, waitFor, within } from '@testing-library/react'; - -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { esDeprecationsMockResponse } from './mocked_responses'; -import { MOCK_REINDEX_DEPRECATION } from './mocked_responses'; - -describe('Cluster settings deprecation flyout', () => { - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - const clusterSettingDeprecation = esDeprecationsMockResponse.migrationsDeprecations[4]; - - const openFlyout = async () => { - fireEvent.click(screen.getAllByTestId('deprecation-clusterSetting')[0]); - return await screen.findByTestId('clusterSettingsDetails'); - }; - - beforeEach(() => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - }); - - test('renders a flyout with deprecation details', async () => { - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - expect(flyout).toBeInTheDocument(); - expect(within(flyout).getByTestId('flyoutTitle')).toHaveTextContent( - clusterSettingDeprecation.message - ); - expect( - (within(flyout).getByTestId('documentationLink') as HTMLAnchorElement).getAttribute('href') - ).toBe(clusterSettingDeprecation.url); - expect(within(flyout).getByTestId('removeClusterSettingsPrompt')).toBeInTheDocument(); - }); - - it('removes deprecated cluster settings', async () => { - httpRequestsMockHelpers.setClusterSettingsResponse({ - acknowledged: true, - persistent: {}, - transietn: {}, - }); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - expect(within(flyout).getByTestId('warningDeprecationBadge')).toBeInTheDocument(); - - fireEvent.click(within(flyout).getByTestId('deleteClusterSettingsButton')); - - expect(httpSetup.post).toHaveBeenLastCalledWith( - `/api/upgrade_assistant/cluster_settings`, - expect.anything() - ); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('clusterSettingsResolutionStatusCell')[0]).toHaveTextContent( - 'Deprecated settings removed' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('clusterSettingsDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Verify prompt to remove setting no longer displays - expect(within(reopenedFlyout).queryByTestId('removeClusterSettingsPrompt')).toBeNull(); - // Verify the action button no longer displays - expect(within(reopenedFlyout).queryByTestId('deleteClusterSettingsButton')).toBeNull(); - // Verify the badge got marked as resolved - expect(within(reopenedFlyout).getByTestId('resolvedDeprecationBadge')).toBeInTheDocument(); - }); - - it('handles failure', async () => { - const error = { - statusCode: 500, - error: 'Remove cluster settings error', - message: 'Remove cluster settings error', - }; - - httpRequestsMockHelpers.setClusterSettingsResponse(undefined, error); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - fireEvent.click(within(flyout).getByTestId('deleteClusterSettingsButton')); - - expect(httpSetup.post).toHaveBeenLastCalledWith( - `/api/upgrade_assistant/cluster_settings`, - expect.anything() - ); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('clusterSettingsResolutionStatusCell')[0]).toHaveTextContent( - 'Settings removal failed' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('clusterSettingsDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Verify the flyout shows an error message - expect(within(reopenedFlyout).getByTestId('deleteClusterSettingsError')).toHaveTextContent( - 'Error deleting cluster settings' - ); - // Verify the remove settings button text changes - expect(within(reopenedFlyout).getByTestId('deleteClusterSettingsButton')).toHaveTextContent( - 'Retry removing deprecated settings' - ); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/data_streams_deprecation.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/data_streams_deprecation.test.ts deleted file mode 100644 index f09a3c3ac8b65..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/data_streams_deprecation.test.ts +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import moment from 'moment'; - -import numeral from '@elastic/numeral'; -import '@testing-library/jest-dom'; -import { fireEvent, screen, waitFor, within } from '@testing-library/react'; - -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { - esDeprecationsMockResponse, - MOCK_DS_DEPRECATION, - MOCK_REINDEX_DEPRECATION, - MOCK_DS_DEPRECATION_REINDEX, - MOCK_DS_DEPRECATION_READ_ONLY, -} from './mocked_responses'; -import { DataStreamMigrationStatus } from '../../../common/data_stream_types'; - -const DATE_FORMAT = 'dddd, MMMM Do YYYY, h:mm:ss a'; -const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; - -const defaultMetaResponse = { - dataStreamName: MOCK_DS_DEPRECATION.index!, - documentationUrl: MOCK_DS_DEPRECATION.url, - lastIndexRequiringUpgradeCreationDate: 1483228800000, - allIndices: ['ds_index'], - allIndicesCount: 1, - indicesRequiringUpgradeCount: 1, - indicesRequiringUpgrade: ['ds_index'], - indicesRequiringUpgradeDocsSize: 51200, - indicesRequiringUpgradeDocsCount: 12, -}; - -const getMetaResponseForDataStream = (dataStreamName: string, documentationUrl: string) => ({ - ...defaultMetaResponse, - dataStreamName, - documentationUrl, -}); - -const defaultMigrationResponse = { - hasRequiredPrivileges: true, - migrationOp: { status: DataStreamMigrationStatus.notStarted }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'reindex', - }, - { - warningType: 'incompatibleDataStream', - resolutionType: 'readonly', - }, - ], -}; - -describe('Data streams deprecation', () => { - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - beforeEach(() => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION.index!, - defaultMigrationResponse - ); - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - defaultMigrationResponse - ); - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - defaultMigrationResponse - ); - - httpRequestsMockHelpers.setDataStreamMetadataResponse( - MOCK_DS_DEPRECATION.index!, - getMetaResponseForDataStream(MOCK_DS_DEPRECATION.index!, MOCK_DS_DEPRECATION.url) - ); - httpRequestsMockHelpers.setDataStreamMetadataResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - getMetaResponseForDataStream( - MOCK_DS_DEPRECATION_REINDEX.index!, - MOCK_DS_DEPRECATION_REINDEX.url - ) - ); - httpRequestsMockHelpers.setDataStreamMetadataResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - getMetaResponseForDataStream( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - MOCK_DS_DEPRECATION_READ_ONLY.url - ) - ); - - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - - httpRequestsMockHelpers.setLoadNodeDiskSpaceResponse([]); - }); - - const setupPage = async () => { - await setupElasticsearchPage(httpSetup); - }; - - const openReindexFlyoutAt = async (index: number) => { - fireEvent.click(screen.getAllByTestId('deprecation-dataStream-reindex')[index]); - await waitFor(() => { - const flyout = - screen.queryByTestId('reindexDataStreamDetails') ?? - screen.queryByTestId('dataStreamMigrationChecklistFlyout'); - - expect(flyout).not.toBeNull(); - }); - - return (screen.queryByTestId('reindexDataStreamDetails') ?? - screen.queryByTestId('dataStreamMigrationChecklistFlyout'))!; - }; - - const openReadOnlyModalAt = async (index: number) => { - fireEvent.click(screen.getAllByTestId('deprecation-dataStream-readonly')[index]); - await waitFor(() => { - const modal = - screen.queryByTestId('updateIndexModal') ?? - screen.queryByTestId('dataStreamMigrationChecklistModal'); - - expect(modal).not.toBeNull(); - }); - - return (screen.queryByTestId('updateIndexModal') ?? - screen.queryByTestId('dataStreamMigrationChecklistModal'))!; - }; - - const checkMigrationWarningCheckbox = async () => { - const checkbox = screen.getByTestId('migrationWarningCheckbox'); - fireEvent.click(within(checkbox).getByRole('checkbox')); - - await waitFor(() => { - expect(screen.getByTestId('startActionButton')).toBeEnabled(); - }); - }; - - describe('reindexing flyout', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - defaultMigrationResponse - ); - httpRequestsMockHelpers.setDataStreamMetadataResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - getMetaResponseForDataStream( - MOCK_DS_DEPRECATION_REINDEX.index!, - MOCK_DS_DEPRECATION_REINDEX.url - ) - ); - }); - it('renders a warning callout if nodes detected with low disk space', async () => { - httpRequestsMockHelpers.setLoadNodeDiskSpaceResponse([ - { - nodeId: '9OFkjpAKS_aPzJAuEOSg7w', - nodeName: 'MacBook-Pro.local', - available: '25%', - }, - ]); - - await setupPage(); - const flyout = await openReindexFlyoutAt(0); - - await within(flyout).findByTestId('dataStreamLastIndexCreationDate'); - - expect(within(flyout).getByTestId('lowDiskSpaceCallout')).toHaveTextContent( - 'Nodes with low disk space' - ); - expect(within(flyout).getAllByTestId('impactedNodeListItem')).toHaveLength(1); - expect(within(flyout).getAllByTestId('impactedNodeListItem')[0]).toHaveTextContent( - 'MacBook-Pro.local (25% available)' - ); - }); - - it('renders a flyout with data stream confirm step for reindex', async () => { - const dataStreamDeprecation = esDeprecationsMockResponse.migrationsDeprecations[6]; - await setupPage(); - const flyout = await openReindexFlyoutAt(1); - - expect(within(flyout).getByTestId('flyoutTitle')).toHaveTextContent( - `${dataStreamDeprecation.index}` - ); - - expect(await screen.findByTestId('dataStreamLastIndexCreationDate')).toHaveTextContent( - `Migration required for indices created on or before${moment( - defaultMetaResponse.lastIndexRequiringUpgradeCreationDate - ).format(DATE_FORMAT)}` - ); - - expect(screen.getByTestId('dataStreamSize')).toHaveTextContent( - `Size${numeral(defaultMetaResponse.indicesRequiringUpgradeDocsSize).format( - FILE_SIZE_DISPLAY_FORMAT - )}` - ); - - expect(screen.getByTestId('dataStreamDocumentCount')).toHaveTextContent( - `Document Count${defaultMetaResponse.indicesRequiringUpgradeDocsCount}` - ); - - expect(screen.getByTestId('dataStreamMigrationWarningsCallout')).toHaveTextContent( - `Indices created on or before ${moment( - defaultMetaResponse.lastIndexRequiringUpgradeCreationDate - ).format(DATE_FORMAT)} need to be reindexed to a compatible format or set to read-only.` - ); - - expect(screen.getByTestId('reindexDsWarningCallout')).toHaveTextContent( - `This operation requires destructive changes that cannot be reversed` - ); - - expect(screen.getByTestId('migrationWarningCheckbox')).toHaveTextContent( - 'Reindex all incompatible data for this data stream' - ); - expect(screen.getByTestId('startActionButton')).toHaveTextContent('Start reindexing'); - expect(screen.getByTestId('startActionButton')).toBeDisabled(); - expect(screen.getByTestId('closeDataStreamConfirmStepButton')).toBeInTheDocument(); - - await checkMigrationWarningCheckbox(); - - expect(screen.getByTestId('startActionButton')).toBeEnabled(); - }); - describe('reindexing progress', () => { - it('reindexing pending', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'reindex', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 0, - pendingCount: 1, - inProgressCount: 0, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'reindex', - }, - ], - } - ); - await setupPage(); - const flyout = await openReindexFlyoutAt(1); - - const checklist = await within(flyout).findByTestId('dataStreamMigrationChecklistFlyout'); - expect(checklist).toHaveTextContent( - `Reindexing ${MOCK_DS_DEPRECATION_REINDEX.index} in progress…` - ); - expect(checklist).toHaveTextContent('0 Indices successfully reindexed.'); - expect(checklist).toHaveTextContent('0 Indices currently getting reindexed.'); - expect(checklist).toHaveTextContent('1 Index waiting to start.'); - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - it('reindexing in progress', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'reindex', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 0, - pendingCount: 0, - inProgressCount: 1, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'reindex', - }, - ], - } - ); - await setupPage(); - const flyout = await openReindexFlyoutAt(1); - - const checklist = await within(flyout).findByTestId('dataStreamMigrationChecklistFlyout'); - expect(checklist).toHaveTextContent('0 Indices successfully reindexed.'); - expect(checklist).toHaveTextContent('1 Index currently getting reindexed.'); - expect(checklist).toHaveTextContent('0 Indices waiting to start.'); - - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - it('reindexing success', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'reindex', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 1, - pendingCount: 0, - inProgressCount: 0, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'reindex', - }, - ], - } - ); - await setupPage(); - const flyout = await openReindexFlyoutAt(1); - - const checklist = await within(flyout).findByTestId('dataStreamMigrationChecklistFlyout'); - expect(checklist).toHaveTextContent('1 Index successfully reindexed.'); - expect(checklist).toHaveTextContent('0 Indices currently getting reindexed.'); - expect(checklist).toHaveTextContent('0 Indices waiting to start.'); - - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - it('reindexing error', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'reindex', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 0, - pendingCount: 0, - inProgressCount: 0, - errorsCount: 1, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'reindex', - }, - ], - } - ); - await setupPage(); - const flyout = await openReindexFlyoutAt(1); - - const checklist = await within(flyout).findByTestId('dataStreamMigrationChecklistFlyout'); - expect(checklist).toHaveTextContent('1 Index failed to get reindexed.'); - expect(checklist).toHaveTextContent('0 Indices successfully reindexed.'); - expect(checklist).toHaveTextContent('0 Indices currently getting reindexed.'); - expect(checklist).toHaveTextContent('0 Indices waiting to start.'); - - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - }); - }); - describe('read-only modal', () => { - beforeEach(() => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - defaultMigrationResponse - ); - httpRequestsMockHelpers.setDataStreamMetadataResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - getMetaResponseForDataStream( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - MOCK_DS_DEPRECATION_READ_ONLY.url - ) - ); - }); - - it('renders a warning callout if nodes detected with low disk space', async () => { - httpRequestsMockHelpers.setLoadNodeDiskSpaceResponse([ - { - nodeId: '9OFkjpAKS_aPzJAuEOSg7w', - nodeName: 'MacBook-Pro.local', - available: '25%', - }, - ]); - - await setupPage(); - const modal = await openReadOnlyModalAt(1); - - await within(modal).findByTestId('readonlyDataStreamModalTitle'); - - expect(within(modal).getByTestId('lowDiskSpaceCallout')).toHaveTextContent( - 'Nodes with low disk space' - ); - expect(within(modal).getAllByTestId('impactedNodeListItem')).toHaveLength(1); - expect(within(modal).getAllByTestId('impactedNodeListItem')[0]).toHaveTextContent( - 'MacBook-Pro.local (25% available)' - ); - }); - - it('renders a modal with data stream confirm step for read-only', async () => { - await setupPage(); - const modal = await openReadOnlyModalAt(1); - - expect(await within(modal).findByTestId('readOnlyDsWarningCallout')).toHaveTextContent( - 'Setting this data to read-only could affect some of the existing setups' - ); - - expect(screen.getByTestId('migrationWarningCheckbox')).toHaveTextContent( - 'Reindex all incompatible data for this data stream' - ); - expect(screen.getByTestId('startActionButton')).toHaveTextContent('Set all to read-only'); - expect(screen.getByTestId('startActionButton')).toBeDisabled(); - expect(within(modal).getByTestId('cancelDataStreamMigrationModal')).toBeInTheDocument(); - - await checkMigrationWarningCheckbox(); - - expect(screen.getByTestId('startActionButton')).toBeEnabled(); - }); - describe('read-only progress', () => { - it('pending', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'readonly', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, - successCount: 0, - pendingCount: 1, - inProgressCount: 0, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'readonly', - }, - ], - } - ); - await setupPage(); - await openReadOnlyModalAt(1); - - const checklist = screen.getByTestId('dataStreamMigrationChecklistModal'); - expect(checklist).toHaveTextContent( - `Setting to read-only ${MOCK_DS_DEPRECATION_READ_ONLY.index} in progress…` - ); - expect(checklist).toHaveTextContent('0 Indices successfully set to read-only.'); - expect(checklist).toHaveTextContent('0 Indices currently getting set to read-only.'); - expect(checklist).toHaveTextContent('1 Index waiting to start.'); - - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - it('read-only in progress', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'readonly', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 0, - pendingCount: 0, - inProgressCount: 1, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'readonly', - }, - ], - } - ); - await setupPage(); - await openReadOnlyModalAt(1); - - const checklist = screen.getByTestId('dataStreamMigrationChecklistModal'); - expect(checklist).toHaveTextContent('0 Indices successfully set to read-only.'); - expect(checklist).toHaveTextContent('1 Index currently getting set to read-only.'); - expect(checklist).toHaveTextContent('0 Indices waiting to start.'); - - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - it('read-only success', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'readonly', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 1, - pendingCount: 0, - inProgressCount: 0, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'readonly', - }, - ], - } - ); - await setupPage(); - await openReadOnlyModalAt(1); - - const checklist = screen.getByTestId('dataStreamMigrationChecklistModal'); - expect(checklist).toHaveTextContent('1 Index successfully set to read-only.'); - expect(checklist).toHaveTextContent('0 Indices currently getting set to read-only.'); - expect(checklist).toHaveTextContent('0 Indices waiting to start.'); - - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - it('reindexing error', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'readonly', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, - successCount: 0, - pendingCount: 0, - inProgressCount: 0, - errorsCount: 1, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'readonly', - }, - ], - } - ); - await setupPage(); - await openReadOnlyModalAt(1); - - const checklist = screen.getByTestId('dataStreamMigrationChecklistModal'); - - expect(checklist).toHaveTextContent('1 Index failed to get set to read-only.'); - expect(checklist).toHaveTextContent('0 Indices successfully set to read-only.'); - expect(checklist).toHaveTextContent('0 Indices currently getting set to read-only.'); - expect(checklist).toHaveTextContent('0 Indices waiting to start.'); - - expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); - expect(screen.getByTestId('cancelDataStreamMigrationButton')).toBeInTheDocument(); - }); - }); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts deleted file mode 100644 index d78b68aaabd6a..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/default_deprecation_flyout.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { fireEvent, screen, within } from '@testing-library/react'; - -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; - -describe('Default deprecation flyout', () => { - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - - beforeEach(() => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'idle', - }); - httpRequestsMockHelpers.setReindexStatusResponse('reindex_index', { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - }); - - test('renders a flyout with deprecation details', async () => { - const multiFieldsDeprecation = esDeprecationsMockResponse.migrationsDeprecations[2]; - await setupElasticsearchPage(httpSetup); - - fireEvent.click(screen.getAllByTestId('deprecation-default')[0]); - - const flyout = await screen.findByTestId('defaultDeprecationDetails'); - - expect(within(flyout).getByTestId('flyoutTitle')).toHaveTextContent( - multiFieldsDeprecation.message - ); - expect( - (within(flyout).getByTestId('documentationLink') as HTMLAnchorElement).getAttribute('href') - ).toBe(multiFieldsDeprecation.url); - expect(within(flyout).getByTestId('flyoutDescription')).toHaveTextContent( - String(multiFieldsDeprecation.index) - ); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts deleted file mode 100644 index f3bd67e18e2df..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/deprecations_list.test.ts +++ /dev/null @@ -1,994 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { fireEvent, screen, waitFor, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { API_BASE_PATH } from '../../../common/constants'; -import type { - ESUpgradeStatus, - EnrichedDeprecationInfo, - MlAction, - ReindexAction, - UnfreezeAction, -} from '../../../common/types'; -import { DataStreamMigrationStatus } from '../../../common/types'; -import { ReindexStep } from '@kbn/reindex-service-plugin/common'; -import { ReindexStatus } from '@kbn/upgrade-assistant-pkg-common'; -import { REINDEX_SERVICE_BASE_PATH } from '@kbn/reindex-service-plugin/server'; -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { - esDeprecationsMockResponse, - MOCK_SNAPSHOT_ID, - MOCK_JOB_ID, - createEsDeprecationsMockResponse, - MOCK_DS_DEPRECATION, - MOCK_DS_DEPRECATION_REINDEX, - MOCK_DS_DEPRECATION_READ_ONLY, -} from './mocked_responses'; - -// Failing: See https://github.com/elastic/kibana/issues/248433 -describe.skip('ES deprecations table', () => { - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - - const waitForResolutionCellsToSettle = async () => { - if (screen.queryAllByTestId('deprecationTableRow').length === 0) { - return; - } - - // Wait on a visible UI boundary that indicates mount-time status providers have settled. - // (Avoids act warnings without introducing fake timers.) - await waitFor(() => { - expect(screen.queryAllByText('Loading status…')).toHaveLength(0); - }); - - await waitFor(() => { - const indexResolutionCells = screen.queryAllByTestId('reindexTableCell-correctiveAction'); - const dataStreamResolutionCells = screen.queryAllByTestId( - 'dataStreamReindexTableCell-correctiveAction' - ); - - for (const cell of [...indexResolutionCells, ...dataStreamResolutionCells]) { - expect(cell).not.toHaveTextContent('Loading status…'); - } - }); - }; - - const setupPage = async () => { - await setupElasticsearchPage(httpSetup); - // Avoid act() warnings from async status-fetch effects by waiting for the resolution cells - // to exit their "Loading status…" placeholder state. - await waitForResolutionCellsToSettle(); - }; - - beforeEach(() => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'idle', - }); - httpRequestsMockHelpers.setReindexStatusResponse('reindex_index', { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - httpRequestsMockHelpers.setLoadRemoteClustersResponse([]); - }); - - afterEach(async () => { - await waitForResolutionCellsToSettle(); - }); - - it('renders deprecations', async () => { - await setupPage(); - - // Verify container exists - expect(screen.getByTestId('esDeprecationsContent')).toBeInTheDocument(); - - // Verify all deprecations appear in the table - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - esDeprecationsMockResponse.migrationsDeprecations.length - ); - }); - - it('refreshes deprecation data', async () => { - await setupPage(); - fireEvent.click(screen.getByTestId('refreshButton')); - - const mlDeprecation = esDeprecationsMockResponse.migrationsDeprecations[0]; - const reindexDeprecation = esDeprecationsMockResponse.migrationsDeprecations[3]; - - // Since upgradeStatusMockResponse includes ML and reindex actions (which require fetching status), there will be 4 requests made - await waitFor(() => { - expect(httpSetup.get).toHaveBeenCalledWith( - `${API_BASE_PATH}/es_deprecations`, - expect.anything() - ); - expect(httpSetup.get).toHaveBeenCalledWith( - `${API_BASE_PATH}/ml_snapshots/${(mlDeprecation.correctiveAction as MlAction).jobId}/${ - (mlDeprecation.correctiveAction as MlAction).snapshotId - }`, - expect.anything() - ); - expect(httpSetup.get).toHaveBeenCalledWith( - `${REINDEX_SERVICE_BASE_PATH}/${reindexDeprecation.index}`, - expect.anything() - ); - expect(httpSetup.get).toHaveBeenCalledWith( - `${API_BASE_PATH}/ml_upgrade_mode`, - expect.anything() - ); - }); - }); - - it('shows critical and warning deprecations count', async () => { - await setupPage(); - - const criticalDeprecations = esDeprecationsMockResponse.migrationsDeprecations.filter( - (deprecation) => deprecation.level === 'critical' - ); - const warningDeprecations = esDeprecationsMockResponse.migrationsDeprecations.filter( - (deprecation) => deprecation.level !== 'critical' - ); - - expect(screen.getByTestId('criticalDeprecationsCount')).toHaveTextContent( - String(criticalDeprecations.length) - ); - expect(screen.getByTestId('warningDeprecationsCount')).toHaveTextContent( - String(warningDeprecations.length) - ); - }); - - describe('remote clusters callout', () => { - beforeEach(() => { - httpRequestsMockHelpers.setLoadRemoteClustersResponse(['test_remote_cluster']); - }); - - it('shows a warning message if a user has remote clusters configured', async () => { - await setupPage(); - // Verify warning exists - expect(screen.getByTestId('remoteClustersWarningCallout')).toBeInTheDocument(); - }); - }); - - describe('search bar', () => { - let user: ReturnType; - - beforeEach(() => { - user = userEvent.setup(); - }); - - const clickFilterByIndex = async (index: number) => { - const searchBar = screen.getByTestId('searchBarContainer'); - const filterButtons = searchBar.querySelectorAll('button.euiFilterButton'); - - expect(filterButtons[index]).toBeDefined(); - await user.click(filterButtons[index] as HTMLButtonElement); - }; - - const clickFilterByTitle = async (title: string) => { - const filterButton = await waitFor(() => { - const el: HTMLButtonElement | null = document.body.querySelector( - `.euiSelectableListItem[title="${title}"]` - ); - expect(el).not.toBeNull(); - return el!; - }); - - await user.click(filterButton); - }; - - const setSearchInputValue = async (searchValue: string) => { - const input = within(screen.getByTestId('searchBarContainer')).getByRole('searchbox'); - fireEvent.change(input, { target: { value: searchValue } }); - fireEvent.keyUp(input, { target: { value: searchValue } }); - }; - - it('filters results by status', async () => { - await setupPage(); - - await clickFilterByIndex(0); // status filter is first - await clickFilterByTitle('Critical'); - - const criticalDeprecations = esDeprecationsMockResponse.migrationsDeprecations.filter( - (deprecation) => deprecation.level === 'critical' - ); - - await waitFor(() => - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - criticalDeprecations.length - ) - ); - - await clickFilterByIndex(0); - await clickFilterByTitle('Critical'); // Reset filter - - await waitFor(() => - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - esDeprecationsMockResponse.migrationsDeprecations.length - ) - ); - }); - - it('filters results by type', async () => { - await setupPage(); - - await clickFilterByIndex(1); // type filter is second - await clickFilterByTitle('Cluster'); - - const clusterDeprecations = esDeprecationsMockResponse.migrationsDeprecations.filter( - (deprecation) => deprecation.type === 'cluster_settings' - ); - - await waitFor(() => - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - clusterDeprecations.length - ) - ); - }); - - it('filters results by query string', async () => { - const multiFieldsDeprecation = esDeprecationsMockResponse.migrationsDeprecations[2]; - - await setupPage(); - await setSearchInputValue(multiFieldsDeprecation.message); - - await waitFor(() => expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength(1)); - expect(screen.getAllByTestId('deprecationTableRow')[0]).toHaveTextContent( - multiFieldsDeprecation.message - ); - }); - - it('shows error for invalid search queries', async () => { - await setupPage(); - - await setSearchInputValue('%'); - - expect(screen.getByTestId('invalidSearchQueryMessage')).toBeInTheDocument(); - expect(screen.getByTestId('invalidSearchQueryMessage')).toHaveTextContent('Invalid search'); - }); - - it('shows message when search query does not return results', async () => { - await setupPage(); - - await setSearchInputValue('foobarbaz'); - - expect(screen.getByTestId('noDeprecationsRow')).toBeInTheDocument(); - expect(screen.getByTestId('noDeprecationsRow')).toHaveTextContent( - 'No Elasticsearch deprecation issues found' - ); - }); - }); - - describe('pagination', () => { - let user: ReturnType; - - const esDeprecationsMockResponseWithManyDeprecations = createEsDeprecationsMockResponse(20); - const { migrationsDeprecations } = esDeprecationsMockResponseWithManyDeprecations; - - const getPaginationItemsCount = () => { - const pagination = screen.getByTestId('esDeprecationsPagination'); - return pagination.querySelectorAll('.euiPagination__item').length; - }; - - const waitForStatusCellsToSettle = async () => { - // Fast-path: if there is no loading placeholder visible, there is nothing to wait for. - if (screen.queryAllByText('Loading status…').length === 0) { - return; - } - await waitFor(() => { - expect(screen.queryAllByText('Loading status…')).toHaveLength(0); - }); - }; - - beforeEach(() => { - user = userEvent.setup(); - httpRequestsMockHelpers.setLoadEsDeprecationsResponse( - esDeprecationsMockResponseWithManyDeprecations - ); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'idle', - }); - }); - - it('shows the correct number of pages and deprecations per page', async () => { - await setupPage(); - - expect(getPaginationItemsCount()).toEqual( - Math.round(migrationsDeprecations.length / 50) // Default rows per page is 50 - ); - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength(50); - - // Navigate to the next page - await user.click(screen.getByTestId('pagination-button-1')); - - // On the second (last) page, we expect to see the remaining deprecations - await waitFor(() => - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - migrationsDeprecations.length - 50 - ) - ); - await waitForStatusCellsToSettle(); - }); - - it('allows the number of viewable rows to change', async () => { - await setupPage(); - await user.click(screen.getByTestId('tablePaginationPopoverButton')); - - // Rows-per-page options are rendered in a portal; query the whole document. - const rowsPerPageButton = await screen.findByTestId('tablePagination-100-rows'); - - await user.click(rowsPerPageButton); - - await waitFor(() => { - expect(getPaginationItemsCount()).toEqual( - Math.round(migrationsDeprecations.length / 100) // Rows per page is now 100 - ); - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - migrationsDeprecations.length - ); - }); - await waitForStatusCellsToSettle(); - }, 7000); - - it('updates pagination when filters change', async () => { - const criticalDeprecations = migrationsDeprecations.filter( - (deprecation) => deprecation.level === 'critical' - ); - - await setupPage(); - - // Status filter is the first filter button - const searchBar = screen.getByTestId('searchBarContainer'); - const filterButtons = searchBar.querySelectorAll('button.euiFilterButton'); - await user.click(filterButtons[0] as HTMLButtonElement); - - const criticalFilterButton = await waitFor(() => { - const el: HTMLButtonElement | null = document.body.querySelector( - `.euiSelectableListItem[title="Critical"]` - ); - expect(el).not.toBeNull(); - return el!; - }); - - await user.click(criticalFilterButton); - - // Only 40 critical deprecations, so only one page should show - await waitFor(() => { - expect(getPaginationItemsCount()).toEqual(1); - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - criticalDeprecations.length - ); - }); - await waitForStatusCellsToSettle(); - }, 7000); - - it('updates pagination on search', async () => { - const reindexDeprecations = migrationsDeprecations.filter( - (deprecation) => deprecation.correctiveAction?.type === 'reindex' - ); - - await setupPage(); - - const input = within(screen.getByTestId('searchBarContainer')).getByRole('searchbox'); - fireEvent.change(input, { target: { value: 'Index created before 7.0' } }); - fireEvent.keyUp(input, { target: { value: 'Index created before 7.0' } }); - - // Only 20 deprecations that match, so only one page should show - await waitFor(() => { - expect(getPaginationItemsCount()).toEqual(1); - expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - reindexDeprecations.length - ); - }); - await waitForStatusCellsToSettle(); - }); - - it('maintains correct row state across pagination', async () => { - await setupPage(); - - const getFirstRowMessageCellText = () => { - const row = screen.getAllByTestId('deprecationTableRow')[0]; - const messageCell = row.querySelector('[data-test-subj$="-message"]'); - expect(messageCell).not.toBeNull(); - return messageCell!.textContent; - }; - - // Verify we have multiple pages - expect(getPaginationItemsCount()).toBeGreaterThan(1); - - // Get the message of the first deprecation on page 1 - const firstDeprecationMessagePage1 = getFirstRowMessageCellText(); - - // Navigate to page 2 - await user.click(screen.getByTestId('pagination-button-1')); - - // Wait for the first row message to change on page 2 - await waitFor(() => { - expect(getFirstRowMessageCellText()).not.toEqual(firstDeprecationMessagePage1); - }); - const firstDeprecationMessagePage2 = getFirstRowMessageCellText(); - - // The first items on different pages should be different - expect(firstDeprecationMessagePage1).not.toEqual(firstDeprecationMessagePage2); - - // Navigate back to page 1 - await user.click(screen.getByTestId('pagination-button-0')); - - // Verify the first deprecation on page 1 is still the same - await waitFor(() => { - expect(getFirstRowMessageCellText()).toEqual(firstDeprecationMessagePage1); - }); - await waitForStatusCellsToSettle(); - }); - }); - - describe('no deprecations', () => { - beforeEach(() => { - const noDeprecationsResponse = { - totalCriticalDeprecations: 0, - migrationsDeprecations: [], - totalCriticalHealthIssues: 0, - enrichedHealthIndicators: [], - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(noDeprecationsResponse); - }); - - test('renders prompt', async () => { - await setupPage(); - - expect(screen.getByTestId('noDeprecationsPrompt')).toBeInTheDocument(); - expect(screen.getByTestId('noDeprecationsPrompt')).toHaveTextContent( - 'Your Elasticsearch configuration is up to date' - ); - }); - }); - - describe('recommended actions for indices', () => { - // Helper to DRY up repeated setup - const setupRecommendedActionTest = async ({ - correctiveAction, - reindexMeta = {}, - deprecationIndex = 3, - }: { - correctiveAction: any; - reindexMeta?: Partial; - deprecationIndex?: number; - }) => { - httpRequestsMockHelpers.setLoadEsDeprecationsResponse({ - ...esDeprecationsMockResponse, - migrationsDeprecations: esDeprecationsMockResponse.migrationsDeprecations.map( - (deprecation, idx) => - idx === deprecationIndex - ? ({ - level: 'critical', - resolveDuringUpgrade: false, - type: 'index_settings', - message: 'Index created before 7.0', - details: 'deprecation details', - url: 'doc_url', - index: correctiveAction.index || 'reindex_index', - correctiveAction, - } as EnrichedDeprecationInfo) - : deprecation - ), - } as ESUpgradeStatus); - - if (correctiveAction.type === 'reindex' || correctiveAction.type === 'unfreeze') { - httpRequestsMockHelpers.setReindexStatusResponse( - correctiveAction.index || 'reindex_index', - { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: reindexMeta.indexName || 'foo', - reindexName: reindexMeta.reindexName || 'reindexed-foo', - aliases: [], - isFrozen: reindexMeta.isFrozen || false, - isReadonly: reindexMeta.isReadonly || false, - isInDataStream: reindexMeta.isInDataStream || false, - isFollowerIndex: reindexMeta.isFollowerIndex || false, - ...reindexMeta, - }, - } - ); - } - - await setupPage(); - }; - - it('recommends set unfreeze if index is frozen', async () => { - await setupRecommendedActionTest({ - correctiveAction: { - type: 'unfreeze', - index: 'reindex_index', - metadata: { - isClosedIndex: false, - isFrozenIndex: true, - isInDataStream: false, - }, - }, - }); - expect(screen.getAllByTestId('reindexTableCell-correctiveAction')).toHaveLength(1); - expect(screen.getByTestId('reindexTableCell-correctiveAction')).toHaveTextContent( - 'Recommended: unfreeze' - ); - }); - - it('recommends set as read-only if index is a follower index', async () => { - await setupRecommendedActionTest({ - correctiveAction: { - type: 'reindex', - index: 'reindex_index', - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - }, - }, - reindexMeta: { - isFollowerIndex: true, - indexName: 'follower-index', - reindexName: 'reindexed-follower-index', - }, - }); - expect(screen.getAllByTestId('reindexTableCell-correctiveAction')).toHaveLength(1); - expect(screen.getByTestId('reindexTableCell-correctiveAction')).toHaveTextContent( - 'Recommended: set to read-only' - ); - }); - - it('recommends set as read-only if index is bigger than 1GB', async () => { - await setupRecommendedActionTest({ - correctiveAction: { - type: 'reindex', - index: 'reindex_index', - indexSizeInBytes: 1173741824, // > 1GB - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - }, - }, - reindexMeta: { - indexName: 'large-index', - reindexName: 'reindexed-large-index', - }, - }); - expect(screen.getAllByTestId('reindexTableCell-correctiveAction')).toHaveLength(1); - expect(screen.getByTestId('reindexTableCell-correctiveAction')).toHaveTextContent( - 'Recommended: set to read-only' - ); - }); - - it('recommends reindexing if index is already read-only', async () => { - await setupRecommendedActionTest({ - correctiveAction: { - type: 'reindex', - index: 'reindex_index', - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - }, - }, - reindexMeta: { - isReadonly: true, - indexName: 'readonly-index', - reindexName: 'reindexed-readonly-index', - }, - }); - expect(screen.getAllByTestId('reindexTableCell-correctiveAction')).toHaveLength(1); - expect(screen.getByTestId('reindexTableCell-correctiveAction')).toHaveTextContent( - 'Recommended: reindex' - ); - }); - - it('recommends set as read-only if reindexing is excluded', async () => { - await setupRecommendedActionTest({ - correctiveAction: { - type: 'reindex', - index: 'reindex_index', - excludedActions: ['readOnly'], - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - }, - }, - reindexMeta: { - indexName: 'excluded-index', - reindexName: 'reindexed-excluded-index', - }, - }); - expect(screen.getAllByTestId('reindexTableCell-correctiveAction')).toHaveLength(1); - expect(screen.getByTestId('reindexTableCell-correctiveAction')).toHaveTextContent( - 'Recommended: reindex' - ); - }); - - it('recommends manual fix if follower index and already read-only', async () => { - await setupRecommendedActionTest({ - correctiveAction: { - type: 'reindex', - index: 'large_and_readonly_index', - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - }, - }, - reindexMeta: { - isFollowerIndex: true, - isReadonly: true, - indexName: 'large_and_readonly_index', - reindexName: 'reindexed-large_and_readonly_index', - }, - }); - expect(screen.getAllByTestId('reindexTableCell-correctiveAction')).toHaveLength(1); - expect(screen.getByTestId('reindexTableCell-correctiveAction')).toHaveTextContent( - 'Resolve manually' - ); - }); - it('recommends reindexing by default', async () => { - await setupRecommendedActionTest({ - correctiveAction: { - type: 'reindex', - index: 'reindex_index', - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - }, - }, - }); - expect(screen.getAllByTestId('reindexTableCell-correctiveAction')).toHaveLength(1); - expect(screen.getByTestId('reindexTableCell-correctiveAction')).toHaveTextContent( - 'Recommended: reindex' - ); - }); - }); - describe('recommended actions for data streams', () => { - it('recommends read-only by default', async () => { - await setupPage(); - - expect( - screen.getAllByTestId('dataStreamReindexTableCell-correctiveAction')[0] - ).toHaveTextContent('Recommended: set to read-only'); - }); - - it('recommends reindexing if read-only is excluded', async () => { - await setupPage(); - - expect( - screen.getAllByTestId('dataStreamReindexTableCell-correctiveAction')[1] - ).toHaveTextContent('Recommended: reindex'); - }); - }); - - describe('action buttons', () => { - describe('frozen indices', () => { - beforeEach(() => { - httpRequestsMockHelpers.setLoadEsDeprecationsResponse({ - ...esDeprecationsMockResponse, - migrationsDeprecations: esDeprecationsMockResponse.migrationsDeprecations.map( - (deprecation) => - deprecation === esDeprecationsMockResponse.migrationsDeprecations[3] - ? ({ - level: 'critical', - resolveDuringUpgrade: false, - type: 'index_settings', - message: 'Index created before 7.0', - details: 'deprecation details', - url: 'doc_url', - index: 'reindex_index', - correctiveAction: { - type: 'unfreeze', - metadata: { - isClosedIndex: false, - isFrozenIndex: true, - isInDataStream: false, - }, - } as UnfreezeAction, - } as EnrichedDeprecationInfo) - : deprecation - ), - } as ESUpgradeStatus); - - httpRequestsMockHelpers.setUpdateIndexResponse( - esDeprecationsMockResponse.migrationsDeprecations[3].index!, - { data: '', error: null } - ); - }); - it('it displays reindexing and unfreeze button for frozen index', async () => { - await setupPage(); - - expect(screen.getAllByTestId('reindexTableCell-actions')).toHaveLength(1); - - expect(screen.queryByTestId('deprecation-unfreeze-unfreeze')).not.toBeNull(); - expect(screen.queryByTestId('deprecation-unfreeze-reindex')).not.toBeNull(); - }); - it('it only displays reindexing button if reindex in progress', async () => { - httpRequestsMockHelpers.setReindexStatusResponse( - esDeprecationsMockResponse.migrationsDeprecations[3].index!, - { - reindexOp: { - status: ReindexStatus.inProgress, - lastCompletedStep: ReindexStep.readonly, - reindexTaskPercComplete: null, - }, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - isFrozen: false, - isReadonly: false, - isInDataStream: false, - isFollowerIndex: false, - }, - } - ); - - await setupPage(); - - // Ensure the async status load has completed before the test ends (prevents act warnings - // from a pending updateStatus() resolving after unmount). - await waitFor(() => { - expect(screen.getByText(/Reindexing in progress/)).toBeInTheDocument(); - }); - - expect(screen.getAllByTestId('reindexTableCell-actions')).toHaveLength(1); - - expect(screen.queryByTestId('deprecation-unfreeze-unfreeze')).toBeNull(); - expect(screen.queryByTestId('deprecation-unfreeze-reindex')).not.toBeNull(); - }); - it('it only displays unfreeze button if unfreezing in progress', async () => { - await setupPage(); - - fireEvent.click(screen.getAllByTestId('deprecation-unfreeze-unfreeze')[0]); - const modal = await screen.findByTestId('updateIndexModal'); - fireEvent.click(within(modal).getByTestId('startIndexUnfreezeButton')); - - expect(screen.getAllByTestId('reindexTableCell-actions')).toHaveLength(1); - - await waitFor(() => { - expect(screen.queryByTestId('deprecation-unfreeze-unfreeze')).not.toBeNull(); - expect(screen.queryByTestId('deprecation-unfreeze-reindex')).toBeNull(); - }); - }); - }); - describe('reindexing indices', () => { - const setupReindexingTest = async ({ - excludedActions = [], - index = 'reindex_index', - metaOverrides = {}, - }: { - excludedActions?: string[]; - index?: string; - metaOverrides?: Partial; - } = {}) => { - httpRequestsMockHelpers.setLoadEsDeprecationsResponse({ - ...esDeprecationsMockResponse, - migrationsDeprecations: esDeprecationsMockResponse.migrationsDeprecations.map( - (deprecation) => - deprecation === esDeprecationsMockResponse.migrationsDeprecations[3] - ? ({ - level: 'critical', - resolveDuringUpgrade: false, - type: 'index_settings', - message: 'Index created before 7.0', - details: 'deprecation details', - url: 'doc_url', - index, - correctiveAction: { - type: 'reindex', - excludedActions, - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - ...metaOverrides, - }, - } as ReindexAction, - } as EnrichedDeprecationInfo) - : deprecation - ), - } as ESUpgradeStatus); - - httpRequestsMockHelpers.setReindexStatusResponse(index, { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'excluded-index', - reindexName: 'reindexed-excluded-index', - aliases: [], - isFrozen: false, - isReadonly: false, - isInDataStream: false, - isFollowerIndex: false, - ...metaOverrides, - }, - }); - - await setupPage(); - }; - - it('it displays reindexing and readonly for indices if both are valid', async () => { - await setupReindexingTest(); - expect(screen.getAllByTestId('reindexTableCell-actions')).toHaveLength(1); - expect(screen.queryByTestId('deprecation-reindex-readonly')).not.toBeNull(); - expect(screen.queryByTestId('deprecation-reindex-reindex')).not.toBeNull(); - }); - it('only displays read-only button if reindexing is excluded', async () => { - await setupReindexingTest({ excludedActions: ['readOnly'] }); - expect(screen.getAllByTestId('reindexTableCell-actions')).toHaveLength(1); - expect(screen.queryByTestId('deprecation-reindex-readonly')).toBeNull(); - expect(screen.queryByTestId('deprecation-reindex-reindex')).not.toBeNull(); - }); - it('only displays read-only button if index is a follower index', async () => { - await setupReindexingTest({ metaOverrides: { isFollowerIndex: true } }); - expect(screen.getAllByTestId('reindexTableCell-actions')).toHaveLength(1); - expect(screen.queryByTestId('deprecation-reindex-readonly')).not.toBeNull(); - expect(screen.queryByTestId('deprecation-reindex-reindex')).toBeNull(); - }); - it('only displays reindex button if read-only is excluded', async () => { - await setupReindexingTest({ - excludedActions: ['reindex'], - index: 'readonly_index', - }); - expect(screen.getAllByTestId('reindexTableCell-actions')).toHaveLength(1); - expect(screen.queryByTestId('deprecation-reindex-readonly')).not.toBeNull(); - expect(screen.queryByTestId('deprecation-reindex-reindex')).toBeNull(); - }); - it('it only displays readonly button if readonly in progress', async () => { - await setupReindexingTest(); - - fireEvent.click(screen.getAllByTestId('deprecation-reindex-readonly')[0]); - const modal = await screen.findByTestId('updateIndexModal'); - fireEvent.click(within(modal).getByTestId('startIndexReadonlyButton')); - - await waitFor(() => { - expect(screen.queryByTestId('deprecation-reindex-readonly')).not.toBeNull(); - expect(screen.queryByTestId('deprecation-reindex-reindex')).toBeNull(); - }); - }); - }); - }); - describe('datastreams', () => { - const defaultMigrationResponse = { - hasRequiredPrivileges: true, - migrationOp: { status: DataStreamMigrationStatus.notStarted }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'reindex', - }, - { - warningType: 'incompatibleDataStream', - resolutionType: 'readonly', - }, - ], - }; - beforeEach(() => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION.index!, - defaultMigrationResponse - ); - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_READ_ONLY.index!, - defaultMigrationResponse - ); - - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse( - MOCK_DS_DEPRECATION_REINDEX.index!, - defaultMigrationResponse - ); - }); - - it('displays read-only and reindex depending if both are valid', async () => { - await setupPage(); - - const actionsCell = screen.getAllByTestId('dataStreamReindexTableCell-actions')[0]; - expect(within(actionsCell).queryByTestId('deprecation-dataStream-reindex')).not.toBeNull(); - expect(within(actionsCell).queryByTestId('deprecation-dataStream-readonly')).not.toBeNull(); - }); - - it('recommends reindexing if read-only is excluded', async () => { - await setupPage(); - - const actionsCell = screen.getAllByTestId('dataStreamReindexTableCell-actions')[1]; - expect(within(actionsCell).queryByTestId('deprecation-dataStream-reindex')).not.toBeNull(); - expect(within(actionsCell).queryByTestId('deprecation-dataStream-readonly')).toBeNull(); - }); - - it('recommends readonly if reindex is excluded', async () => { - await setupPage(); - - const actionsCell = screen.getAllByTestId('dataStreamReindexTableCell-actions')[2]; - expect(within(actionsCell).queryByTestId('deprecation-dataStream-reindex')).toBeNull(); - expect(within(actionsCell).queryByTestId('deprecation-dataStream-readonly')).not.toBeNull(); - }); - it('only displays reindex button if reindexing is in progress', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse(MOCK_DS_DEPRECATION.index!, { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'reindex', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 0, - pendingCount: 1, - inProgressCount: 0, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'reindex', - }, - ], - }); - await setupPage(); - - const actionsCell = screen.getAllByTestId('dataStreamReindexTableCell-actions')[0]; - expect(within(actionsCell).queryByTestId('deprecation-dataStream-reindex')).not.toBeNull(); - expect(within(actionsCell).queryByTestId('deprecation-dataStream-readonly')).toBeNull(); - }); - it('only displays readonly button if setting read-only is in progress', async () => { - httpRequestsMockHelpers.setDataStreamMigrationStatusResponse(MOCK_DS_DEPRECATION.index!, { - hasRequiredPrivileges: true, - migrationOp: { - resolutionType: 'readonly', - status: DataStreamMigrationStatus.inProgress, - taskPercComplete: 1, - progressDetails: { - startTimeMs: Date.now() - 10000, // now - 10 seconds - successCount: 0, - pendingCount: 1, - inProgressCount: 0, - errorsCount: 0, - }, - }, - warnings: [ - { - warningType: 'incompatibleDataStream', - resolutionType: 'readonly', - }, - ], - }); - await setupPage(); - - const actionsCell = screen.getAllByTestId('dataStreamReindexTableCell-actions')[0]; - expect(within(actionsCell).queryByTestId('deprecation-dataStream-reindex')).toBeNull(); - expect(within(actionsCell).queryByTestId('deprecation-dataStream-readonly')).not.toBeNull(); - }); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts deleted file mode 100644 index ae2b2ab8dd4ce..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/error_handling.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { screen } from '@testing-library/react'; - -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; - -describe('Error handling', () => { - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - - beforeEach(() => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - }); - - it('handles 403', async () => { - const error = { - statusCode: 403, - error: 'Forbidden', - message: 'Forbidden', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await setupElasticsearchPage(httpSetup); - - expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( - 'You are not authorized to view Elasticsearch deprecation issues.' - ); - }); - - it('shows upgraded message when all nodes have been upgraded', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - // This is marked true in the scenario where none of the nodes have the same major version of Kibana, - // and therefore we assume all have been upgraded - allNodesUpgraded: true, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await setupElasticsearchPage(httpSetup); - - expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( - 'All Elasticsearch nodes have been upgraded.' - ); - }); - - it('shows partially upgrade error when nodes are running different versions', async () => { - const error = { - statusCode: 426, - error: 'Upgrade required', - message: 'There are some nodes running a different version of Elasticsearch', - attributes: { - allNodesUpgraded: false, - }, - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await setupElasticsearchPage(httpSetup); - - expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( - 'Upgrade Kibana to the same version as your Elasticsearch cluster. One or more nodes in the cluster is running a different version than Kibana.' - ); - }); - - it('handles generic error', async () => { - const error = { - statusCode: 500, - error: 'Internal server error', - message: 'Internal server error', - }; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(undefined, error); - - await setupElasticsearchPage(httpSetup); - - expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( - 'Could not retrieve Elasticsearch deprecation issues.' - ); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.tsx b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.tsx deleted file mode 100644 index 8cba1edecb8e6..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/es_deprecations.helpers.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { HttpSetup } from '@kbn/core/public'; -import React from 'react'; -import { screen, waitFor } from '@testing-library/react'; -import { renderWithI18n } from '@kbn/test-jest-helpers'; -import { createMemoryHistory } from 'history'; -import { Router, Routes, Route } from '@kbn/shared-ux-router'; -import { act } from 'react-dom/test-utils'; -import { EsDeprecations } from '../../../public/application/components'; -import { WithAppDependencies } from '../helpers/setup_environment'; - -export const setupElasticsearchPage = async ( - httpSetup: HttpSetup, - overrides?: Record -): Promise => { - const EsDeprecationsWithDependencies = WithAppDependencies(EsDeprecations, httpSetup, overrides); - const history = createMemoryHistory({ initialEntries: ['/es_deprecations'] }); - - renderWithI18n( - - - - - - ); - - // In suites that opt into fake timers, some mount-time requests resolve via timers. - // Flush pending timers inside act() so their React state updates are correctly wrapped. - if (jest.isMockFunction(setTimeout) && jest.getTimerCount() > 0) { - await act(async () => { - await jest.runOnlyPendingTimersAsync(); - }); - } - - // Wait for the initial render baseline (mount-time requests/async state). - await waitFor(() => { - const settledElement = - screen.queryByTestId('esDeprecationsContent') ?? - screen.queryByTestId('noDeprecationsPrompt') ?? - screen.queryByTestId('deprecationsPageLoadingError'); - - expect(settledElement).not.toBeNull(); - }); - - // Some providers start in LoadingState.Loading and then resolve status asynchronously. - // If "Loading status…" is present, wait for it to disappear so those mount-time state updates - // happen while RTL is actively awaiting a UI boundary (prevents act warnings). - const hasLoadingStatus = screen.queryAllByText('Loading status…').length > 0; - if (hasLoadingStatus) { - await waitFor(() => { - expect(screen.queryAllByText('Loading status…')).toHaveLength(0); - }); - } - - // Some rows fetch "status" asynchronously on mount (IndexStatusProvider/DataStreamMigrationStatusProvider). - // Wait for table rows to exist first, then wait for the visible boundary that indicates those - // mount-time async updates have settled (prevents React act() warnings). - const hasTable = screen.queryByTestId('esDeprecationsContent') !== null; - if (hasTable) { - await screen.findAllByTestId('deprecationTableRow'); - await waitFor(() => { - const indexResolutionCells = screen.queryAllByTestId('reindexTableCell-correctiveAction'); - const dataStreamResolutionCells = screen.queryAllByTestId( - 'dataStreamReindexTableCell-correctiveAction' - ); - - const resolutionCells = [...indexResolutionCells, ...dataStreamResolutionCells]; - - // If there are no resolution cells on the page, there's nothing to wait for. - if (resolutionCells.length === 0) { - return; - } - - // Ensure async status providers have settled (no "Loading status…" placeholders remaining). - for (const cell of resolutionCells) { - expect(cell).not.toHaveTextContent('Loading status…'); - } - }); - } -}; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts deleted file mode 100644 index 40cb427247366..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/index_settings_deprecation_flyout.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { fireEvent, screen, waitFor, within } from '@testing-library/react'; - -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { - esDeprecationsMockResponse, - MOCK_SNAPSHOT_ID, - MOCK_JOB_ID, - MOCK_REINDEX_DEPRECATION, -} from './mocked_responses'; - -describe('Index settings deprecation flyout', () => { - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - const indexSettingDeprecation = esDeprecationsMockResponse.migrationsDeprecations[1]; - - const openFlyout = async () => { - fireEvent.click(screen.getAllByTestId('deprecation-indexSetting')[0]); - return await screen.findByTestId('indexSettingsDetails'); - }; - - beforeEach(() => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'idle', - }); - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - }); - - it('renders a flyout with deprecation details', async () => { - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - expect(flyout).toBeInTheDocument(); - expect(within(flyout).getByTestId('flyoutTitle')).toHaveTextContent( - indexSettingDeprecation.message - ); - expect( - (within(flyout).getByTestId('documentationLink') as HTMLAnchorElement).getAttribute('href') - ).toBe(indexSettingDeprecation.url); - expect(within(flyout).getByTestId('removeSettingsPrompt')).toBeInTheDocument(); - }); - - it('removes deprecated index settings', async () => { - httpRequestsMockHelpers.setUpdateIndexSettingsResponse(indexSettingDeprecation.index!, { - acknowledged: true, - }); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - expect(within(flyout).getByTestId('warningDeprecationBadge')).toBeInTheDocument(); - - fireEvent.click(within(flyout).getByTestId('deleteSettingsButton')); - - expect(httpSetup.post).toHaveBeenLastCalledWith( - `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings`, - expect.anything() - ); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('indexSettingsResolutionStatusCell')[0]).toHaveTextContent( - 'Deprecated settings removed' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('indexSettingsDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Verify prompt to remove setting no longer displays - expect(within(reopenedFlyout).queryByTestId('removeSettingsPrompt')).toBeNull(); - // Verify the action button no longer displays - expect(within(reopenedFlyout).queryByTestId('deleteSettingsButton')).toBeNull(); - // Verify the badge got marked as resolved - expect(within(reopenedFlyout).getByTestId('resolvedDeprecationBadge')).toBeInTheDocument(); - }); - - it('handles failure', async () => { - const error = { - statusCode: 500, - error: 'Remove index settings error', - message: 'Remove index settings error', - }; - - httpRequestsMockHelpers.setUpdateIndexSettingsResponse( - indexSettingDeprecation.index!, - undefined, - error - ); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - fireEvent.click(within(flyout).getByTestId('deleteSettingsButton')); - - expect(httpSetup.post).toHaveBeenLastCalledWith( - `/api/upgrade_assistant/${indexSettingDeprecation.index!}/index_settings`, - expect.anything() - ); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('indexSettingsResolutionStatusCell')[0]).toHaveTextContent( - 'Settings removal failed' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('indexSettingsDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Verify the flyout shows an error message - expect(within(reopenedFlyout).getByTestId('deleteSettingsError')).toHaveTextContent( - 'Error deleting index settings' - ); - // Verify the remove settings button text changes - expect(within(reopenedFlyout).getByTestId('deleteSettingsButton')).toHaveTextContent( - 'Retry removing deprecated settings' - ); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts deleted file mode 100644 index 5aaeaa9a36b8e..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/ml_snapshots_deprecation_flyout.test.ts +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { fireEvent, screen, waitFor, within } from '@testing-library/react'; - -import type { MlAction } from '../../../common/types'; -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { esDeprecationsMockResponse, MOCK_SNAPSHOT_ID, MOCK_JOB_ID } from './mocked_responses'; - -describe('Machine learning deprecation flyout', () => { - let esDeprecationsResponse: typeof esDeprecationsMockResponse; - let mlDeprecation: (typeof esDeprecationsMockResponse.migrationsDeprecations)[number]; - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - - const openFlyout = async () => { - fireEvent.click(screen.getAllByTestId('deprecation-mlSnapshot')[0]); - return await screen.findByTestId('mlSnapshotDetails'); - }; - - beforeEach(() => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - // Tests can mutate the loaded deprecations in-memory; ensure isolation by cloning per test. - esDeprecationsResponse = JSON.parse(JSON.stringify(esDeprecationsMockResponse)); - mlDeprecation = esDeprecationsResponse.migrationsDeprecations[0]; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsResponse); - httpRequestsMockHelpers.setLoadMlUpgradeModeResponse({ mlUpgradeModeEnabled: false }); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'idle', - }); - httpRequestsMockHelpers.setReindexStatusResponse('reindex_index', { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - }); - - test('renders a flyout with deprecation details', async () => { - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - expect(flyout).toBeInTheDocument(); - expect(within(flyout).getByTestId('flyoutTitle')).toHaveTextContent( - 'Upgrade or delete model snapshot' - ); - expect( - (within(flyout).getByTestId('documentationLink') as HTMLAnchorElement).getAttribute('href') - ).toBe(mlDeprecation.url); - }); - - describe('upgrade snapshots', () => { - it('successfully upgrades snapshots', async () => { - httpRequestsMockHelpers.setUpgradeMlSnapshotResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'in_progress', - }); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - expect(within(flyout).getByTestId('criticalDeprecationBadge')).toBeInTheDocument(); - expect(within(flyout).getByTestId('upgradeSnapshotButton')).toHaveTextContent('Upgrade'); - - fireEvent.click(within(flyout).getByTestId('upgradeSnapshotButton')); - - // After the upgrade request starts, the status polling should resolve as complete. - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'complete', - }); - - // First, we expect a POST request to upgrade the snapshot - await waitFor(() => { - expect(httpSetup.post).toHaveBeenLastCalledWith( - '/api/upgrade_assistant/ml_snapshots', - expect.anything() - ); - }); - - // Next, we expect a GET request to check the status of the upgrade - await waitFor(() => { - expect(httpSetup.get).toHaveBeenLastCalledWith( - `/api/upgrade_assistant/ml_snapshots/${MOCK_JOB_ID}/${MOCK_SNAPSHOT_ID}`, - expect.anything() - ); - }); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('mlActionResolutionCell')[0]).toHaveTextContent( - 'Upgrade complete' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('mlSnapshotDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Flyout actions should be hidden if deprecation was resolved - expect(within(reopenedFlyout).queryByTestId('upgradeSnapshotButton')).toBeNull(); - expect(within(reopenedFlyout).queryByTestId('deleteSnapshotButton')).toBeNull(); - // Badge should be updated in flyout title - expect(within(reopenedFlyout).getByTestId('resolvedDeprecationBadge')).toBeInTheDocument(); - }); - - it('handles upgrade failure', async () => { - const error = { - statusCode: 500, - error: 'Upgrade snapshot error', - message: 'Upgrade snapshot error', - }; - - httpRequestsMockHelpers.setUpgradeMlSnapshotResponse(undefined, error); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'error', - error, - }); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - fireEvent.click(within(flyout).getByTestId('upgradeSnapshotButton')); - - await waitFor(() => { - expect(httpSetup.post).toHaveBeenLastCalledWith( - '/api/upgrade_assistant/ml_snapshots', - expect.anything() - ); - }); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('mlActionResolutionCell')[0]).toHaveTextContent( - 'Upgrade failed' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('mlSnapshotDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Verify the flyout shows an error message - expect(within(reopenedFlyout).getByTestId('resolveSnapshotError')).toHaveTextContent( - 'Error upgrading snapshot' - ); - // Verify the upgrade button text changes - expect(within(reopenedFlyout).getByTestId('upgradeSnapshotButton')).toHaveTextContent( - 'Retry upgrade' - ); - }); - - it('Disables actions if ml_upgrade_mode is enabled', async () => { - httpRequestsMockHelpers.setLoadMlUpgradeModeResponse({ - mlUpgradeModeEnabled: true, - }); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - // Shows an error callout with a docs link - expect(within(flyout).getByTestId('mlUpgradeModeEnabledError')).toBeInTheDocument(); - expect(within(flyout).getByTestId('setUpgradeModeDocsLink')).toBeInTheDocument(); - // Flyout actions should be hidden - expect(within(flyout).queryByTestId('upgradeSnapshotButton')).toBeNull(); - expect(within(flyout).queryByTestId('deleteSnapshotButton')).toBeNull(); - }); - }); - - describe('delete snapshots', () => { - it('successfully deletes snapshots', async () => { - const jobId = (mlDeprecation.correctiveAction! as MlAction).jobId; - const snapshotId = (mlDeprecation.correctiveAction! as MlAction).snapshotId; - httpRequestsMockHelpers.setDeleteMlSnapshotResponse(jobId, snapshotId, { - acknowledged: true, - }); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - expect(within(flyout).getByTestId('criticalDeprecationBadge')).toBeInTheDocument(); - expect(within(flyout).getByTestId('deleteSnapshotButton')).toHaveTextContent('Delete'); - - fireEvent.click(within(flyout).getByTestId('deleteSnapshotButton')); - - await waitFor(() => { - expect(httpSetup.delete).toHaveBeenLastCalledWith( - `/api/upgrade_assistant/ml_snapshots/${jobId}/${snapshotId}`, - expect.anything() - ); - }); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('mlActionResolutionCell')[0]).toHaveTextContent( - 'Deletion complete' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('mlSnapshotDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Flyout actions should be hidden if deprecation was resolved - expect(within(reopenedFlyout).queryByTestId('upgradeSnapshotButton')).toBeNull(); - expect(within(reopenedFlyout).queryByTestId('deleteSnapshotButton')).toBeNull(); - // Badge should be updated in flyout title - expect(within(reopenedFlyout).getByTestId('resolvedDeprecationBadge')).toBeInTheDocument(); - }); - - it('handles delete failure', async () => { - const error = { - statusCode: 500, - error: 'Upgrade snapshot error', - message: 'Upgrade snapshot error', - }; - - const jobId = (mlDeprecation.correctiveAction! as MlAction).jobId; - const snapshotId = (mlDeprecation.correctiveAction! as MlAction).snapshotId; - httpRequestsMockHelpers.setDeleteMlSnapshotResponse(jobId, snapshotId, undefined, error); - - await setupElasticsearchPage(httpSetup); - const flyout = await openFlyout(); - - fireEvent.click(within(flyout).getByTestId('deleteSnapshotButton')); - - await waitFor(() => { - expect(httpSetup.delete).toHaveBeenLastCalledWith( - `/api/upgrade_assistant/ml_snapshots/${jobId}/${snapshotId}`, - expect.anything() - ); - }); - - // Verify the "Resolution" column of the table is updated - await waitFor(() => { - expect(screen.getAllByTestId('mlActionResolutionCell')[0]).toHaveTextContent( - 'Deletion failed' - ); - }); - - await waitFor(() => { - expect(screen.queryByTestId('mlSnapshotDetails')).toBeNull(); - }); - - // Reopen the flyout - const reopenedFlyout = await openFlyout(); - - // Verify the flyout shows an error message - expect(within(reopenedFlyout).getByTestId('resolveSnapshotError')).toHaveTextContent( - 'Error deleting snapshot' - ); - // Verify the upgrade button text changes - expect(within(reopenedFlyout).getByTestId('deleteSnapshotButton')).toHaveTextContent( - 'Retry delete' - ); - }); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts deleted file mode 100644 index 7bcbcc24963e1..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/mocked_responses.ts +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { ESUpgradeStatus, EnrichedDeprecationInfo } from '../../../common/types'; - -export const MOCK_SNAPSHOT_ID = '1'; -export const MOCK_JOB_ID = 'deprecation_check_job'; - -export const MOCK_ML_DEPRECATION: EnrichedDeprecationInfo = { - level: 'critical', - resolveDuringUpgrade: false, - type: 'ml_settings', - message: 'model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded', - details: - 'model snapshot [%s] for job [%s] supports minimum version [%s] and needs to be at least [%s]', - url: 'doc_url', - correctiveAction: { - type: 'mlSnapshot', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - }, -}; - -export const MOCK_REINDEX_DEPRECATION: EnrichedDeprecationInfo = { - level: 'critical', - resolveDuringUpgrade: false, - type: 'index_settings', - message: 'Index created before 7.0', - details: 'deprecation details', - url: 'doc_url', - index: 'reindex_index', - correctiveAction: { - type: 'reindex', - metadata: { - isClosedIndex: false, - isFrozenIndex: false, - isInDataStream: false, - }, - }, -}; - -const MOCK_INDEX_SETTING_DEPRECATION: EnrichedDeprecationInfo = { - level: 'warning', - resolveDuringUpgrade: false, - type: 'index_settings', - message: 'Setting [index.routing.allocation.include._tier] is deprecated', - details: 'deprecation details', - url: 'doc_url', - index: 'my_index', - correctiveAction: { - type: 'indexSetting', - deprecatedSettings: ['index.routing.allocation.include._tier'], - }, -}; - -const MOCK_CLUSTER_SETTING_DEPRECATION: EnrichedDeprecationInfo = { - level: 'warning', - resolveDuringUpgrade: false, - type: 'cluster_settings', - message: 'Setting [cluster.routing.allocation.require._tier] is deprecated', - details: 'deprecation details', - url: 'doc_url', - correctiveAction: { - type: 'clusterSetting', - deprecatedSettings: ['cluster.routing.allocation.require._tier'], - }, -}; - -const MOCK_DEFAULT_DEPRECATION: EnrichedDeprecationInfo = { - level: 'warning', - resolveDuringUpgrade: false, - type: 'index_settings', - message: 'multi-fields within multi-fields', - details: 'deprecation details', - url: 'doc_url', - index: 'nested_multi-fields', -}; - -export const MOCK_DS_DEPRECATION: EnrichedDeprecationInfo = { - index: 'reindex_or_readonly_ds', - type: 'data_streams', - details: 'Data stream deprecation details', - message: 'Outdated data stream', - url: 'doc_url', - level: 'critical', - resolveDuringUpgrade: false, - correctiveAction: { - type: 'dataStream', - metadata: { - excludedActions: [], - reindexRequired: true, - totalBackingIndices: 1, - indicesRequiringUpgradeCount: 1, - indicesRequiringUpgrade: ['ds_index'], - ignoredIndicesRequiringUpgrade: [], - ignoredIndicesRequiringUpgradeCount: 0, - }, - }, -}; - -export const MOCK_DS_DEPRECATION_REINDEX: EnrichedDeprecationInfo = { - index: 'reindex_ds', - type: 'data_streams', - details: 'Data stream deprecation details', - message: 'Outdated data stream', - url: 'doc_url', - level: 'critical', - resolveDuringUpgrade: false, - correctiveAction: { - type: 'dataStream', - metadata: { - excludedActions: ['readOnly'], - reindexRequired: true, - totalBackingIndices: 1, - indicesRequiringUpgradeCount: 1, - indicesRequiringUpgrade: ['ds_index'], - ignoredIndicesRequiringUpgrade: [], - ignoredIndicesRequiringUpgradeCount: 0, - }, - }, -}; - -export const MOCK_DS_DEPRECATION_READ_ONLY: EnrichedDeprecationInfo = { - index: 'readonly_ds', - type: 'data_streams', - details: 'Data stream deprecation details', - message: 'Outdated data stream', - url: 'doc_url', - level: 'critical', - resolveDuringUpgrade: false, - correctiveAction: { - type: 'dataStream', - metadata: { - excludedActions: ['reindex'], - reindexRequired: true, - totalBackingIndices: 1, - indicesRequiringUpgradeCount: 1, - indicesRequiringUpgrade: ['ds_index'], - ignoredIndicesRequiringUpgrade: [], - ignoredIndicesRequiringUpgradeCount: 0, - }, - }, -}; - -export const esDeprecationsMockResponse: ESUpgradeStatus = { - totalCriticalDeprecations: 2, - migrationsDeprecations: [ - MOCK_ML_DEPRECATION, - MOCK_INDEX_SETTING_DEPRECATION, - MOCK_DEFAULT_DEPRECATION, - MOCK_REINDEX_DEPRECATION, - MOCK_CLUSTER_SETTING_DEPRECATION, - MOCK_DS_DEPRECATION, - MOCK_DS_DEPRECATION_REINDEX, - MOCK_DS_DEPRECATION_READ_ONLY, - ], - totalCriticalHealthIssues: 0, - enrichedHealthIndicators: [], -}; - -// Useful for testing pagination where a large number of deprecations are needed -export const createEsDeprecationsMockResponse = ( - numDeprecationsPerType: number -): ESUpgradeStatus => { - const mlDeprecations: EnrichedDeprecationInfo[] = Array.from( - { - length: numDeprecationsPerType, - }, - () => MOCK_ML_DEPRECATION - ); - - const indexSettingsDeprecations: EnrichedDeprecationInfo[] = Array.from( - { - length: numDeprecationsPerType, - }, - () => MOCK_INDEX_SETTING_DEPRECATION - ); - - const reindexDeprecations: EnrichedDeprecationInfo[] = Array.from( - { - length: numDeprecationsPerType, - }, - () => MOCK_REINDEX_DEPRECATION - ); - - const defaultDeprecations: EnrichedDeprecationInfo[] = Array.from( - { - length: numDeprecationsPerType, - }, - () => MOCK_DEFAULT_DEPRECATION - ); - - const migrationsDeprecations: EnrichedDeprecationInfo[] = [ - ...defaultDeprecations, - ...reindexDeprecations, - ...indexSettingsDeprecations, - ...mlDeprecations, - ]; - - return { - totalCriticalDeprecations: mlDeprecations.length + reindexDeprecations.length, - migrationsDeprecations, - totalCriticalHealthIssues: esDeprecationsMockResponse.totalCriticalHealthIssues, - enrichedHealthIndicators: esDeprecationsMockResponse.enrichedHealthIndicators, - }; -}; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/readonly_index_modal.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/readonly_index_modal.test.ts deleted file mode 100644 index e8870f9fe1bfc..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/readonly_index_modal.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { ReindexStatusResponse } from '@kbn/reindex-service-plugin/common'; -import { ReindexStep } from '@kbn/reindex-service-plugin/common'; -import { ReindexStatus } from '@kbn/upgrade-assistant-pkg-common'; -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { - esDeprecationsMockResponse, - MOCK_SNAPSHOT_ID, - MOCK_JOB_ID, - MOCK_REINDEX_DEPRECATION, -} from './mocked_responses'; - -const defaultReindexStatusMeta: ReindexStatusResponse['meta'] = { - indexName: 'foo', - aliases: [], - isFrozen: false, - isReadonly: false, - isInDataStream: false, - isFollowerIndex: false, -}; - -describe('Readonly index modal', () => { - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - let user: ReturnType; - - const openReadOnlyModal = async () => { - await user.click(screen.getAllByTestId('deprecation-reindex-readonly')[0]); - return await screen.findByTestId('updateIndexModal'); - }; - - beforeEach(() => { - user = userEvent.setup(); - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'idle', - }); - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - httpRequestsMockHelpers.setLoadNodeDiskSpaceResponse([]); - }); - - describe('low disk space', () => { - it('renders a warning callout if nodes detected with low disk space', async () => { - httpRequestsMockHelpers.setLoadNodeDiskSpaceResponse([ - { - nodeId: '9OFkjpAKS_aPzJAuEOSg7w', - nodeName: 'MacBook-Pro.local', - available: '25%', - }, - ]); - - await setupElasticsearchPage(httpSetup); - const modal = await openReadOnlyModal(); - - expect(await within(modal).findByTestId('lowDiskSpaceCallout')).toHaveTextContent( - 'Nodes with low disk space' - ); - expect(within(modal).getAllByTestId('impactedNodeListItem')).toHaveLength(1); - expect(within(modal).getAllByTestId('impactedNodeListItem')[0]).toHaveTextContent( - 'MacBook-Pro.local (25% available)' - ); - }); - }); - - describe('readonly', () => { - it('renders a modal with index confirm step for read-only', async () => { - await setupElasticsearchPage(httpSetup); - const modal = await openReadOnlyModal(); - - expect(modal).toBeInTheDocument(); - expect(within(modal).getByTestId('updateIndexModalTitle')).toHaveTextContent( - 'Set index to read-only' - ); - }); - - it('shows success state when marking as readonly an index that has failed to reindex', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: { - status: ReindexStatus.failed, - lastCompletedStep: ReindexStep.reindexCompleted, - reindexTaskPercComplete: 1, - }, - warnings: [], - hasRequiredPrivileges: true, - meta: defaultReindexStatusMeta, - }); - - await setupElasticsearchPage(httpSetup); - const modal = await openReadOnlyModal(); - - await user.click(within(modal).getByTestId('startIndexReadonlyButton')); - - expect(await screen.findByTestId('updateIndexModalTitle')).toHaveTextContent( - 'Setting index to read-only' - ); - - expect(screen.getByTestId('stepProgressStep')).toHaveTextContent( - 'Setting foo index to read-only.' - ); - }); - }); - - describe('follower index', () => { - it('displays follower index callout and only shows mark as read-only button when index is a follower index', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - ...defaultReindexStatusMeta, - isFollowerIndex: true, - }, - }); - - await setupElasticsearchPage(httpSetup); - const modal = await openReadOnlyModal(); - - // Verify follower index callout is displayed - expect(within(modal).getByTestId('followerIndexCallout')).toBeInTheDocument(); - - // Verify only mark as read-only button is available (no reindex button) - expect(within(modal).getByTestId('startIndexReadonlyButton')).toBeInTheDocument(); - expect(within(modal).queryByTestId('startReindexingButton')).toBeNull(); - }); - }); -}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts deleted file mode 100644 index 28e4a5cbfd536..0000000000000 --- a/x-pack/platform/plugins/private/upgrade_assistant/__jest__/client_integration/es_deprecations/reindex_deprecation_flyout.test.ts +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import '@testing-library/jest-dom'; -import { fireEvent, screen, waitFor, within } from '@testing-library/react'; - -import type { ReindexStatusResponse } from '@kbn/reindex-service-plugin/common'; -import { ReindexStep } from '@kbn/reindex-service-plugin/common'; -import { ReindexStatus } from '@kbn/upgrade-assistant-pkg-common'; -import { setupEnvironment } from '../helpers/setup_environment'; -import { setupElasticsearchPage } from './es_deprecations.helpers'; -import { - esDeprecationsMockResponse, - MOCK_SNAPSHOT_ID, - MOCK_JOB_ID, - MOCK_REINDEX_DEPRECATION, -} from './mocked_responses'; - -const defaultReindexStatusMeta: ReindexStatusResponse['meta'] = { - indexName: 'foo', - aliases: [], - isFrozen: false, - isReadonly: false, - isInDataStream: false, - isFollowerIndex: false, -}; - -describe('Reindex deprecation flyout', () => { - afterEach(async () => { - const flyout = screen.queryByTestId('reindexDetails'); - if (flyout) { - const closeButton = within(flyout).getByRole('button', { name: 'Close' }); - fireEvent.click(closeButton); - await waitFor(() => { - expect(screen.queryByTestId('reindexDetails')).toBeNull(); - }); - } - }); - - let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; - let httpSetup: ReturnType['httpSetup']; - - const openReindexFlyout = async () => { - fireEvent.click(screen.getAllByTestId('deprecation-reindex-reindex')[0]); - return await screen.findByTestId('reindexDetails'); - }; - - const proceedToReindexProgress = async () => { - fireEvent.click(screen.getByTestId('startReindexingButton')); - - const warningCheckbox = screen.queryByTestId('warninStepCheckbox'); - if (warningCheckbox) { - fireEvent.click(within(warningCheckbox).getByRole('checkbox')); - - await waitFor(() => { - expect(screen.getByTestId('startReindexingButton')).toBeEnabled(); - }); - - fireEvent.click(screen.getByTestId('startReindexingButton')); - } - }; - - beforeEach(async () => { - const mockEnvironment = setupEnvironment(); - httpRequestsMockHelpers = mockEnvironment.httpRequestsMockHelpers; - httpSetup = mockEnvironment.httpSetup; - - httpRequestsMockHelpers.setLoadEsDeprecationsResponse(esDeprecationsMockResponse); - httpRequestsMockHelpers.setUpgradeMlSnapshotStatusResponse({ - nodeId: 'my_node', - snapshotId: MOCK_SNAPSHOT_ID, - jobId: MOCK_JOB_ID, - status: 'idle', - }); - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: null, - warnings: [], - hasRequiredPrivileges: true, - meta: { - indexName: 'foo', - reindexName: 'reindexed-foo', - aliases: [], - }, - }); - httpRequestsMockHelpers.setLoadNodeDiskSpaceResponse([]); - - await setupElasticsearchPage(httpSetup); - }); - - it('renders error callout when reindex fails', async () => { - httpRequestsMockHelpers.setStartReindexingResponse(MOCK_REINDEX_DEPRECATION.index!, undefined, { - statusCode: 404, - message: 'no such index [test]', - }); - - const flyout = await openReindexFlyout(); - await proceedToReindexProgress(); - - expect(flyout).toBeInTheDocument(); - expect(await within(flyout).findByTestId('reindexingFailedCallout')).toBeInTheDocument(); - }); - - it('renders error callout when fetch status fails', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, undefined, { - statusCode: 404, - message: 'no such index [test]', - }); - - const flyout = await openReindexFlyout(); - await proceedToReindexProgress(); - - expect(flyout).toBeInTheDocument(); - expect(await within(flyout).findByTestId('fetchFailedCallout')).toBeInTheDocument(); - }); - - describe('reindexing progress', () => { - it('renders a flyout with index confirm step for reindex', async () => { - const reindexDeprecation = esDeprecationsMockResponse.migrationsDeprecations[3]; - const flyout = await openReindexFlyout(); - - expect(flyout).toBeInTheDocument(); - expect(within(flyout).getByTestId('flyoutTitle')).toHaveTextContent( - `Reindex ${reindexDeprecation.index}` - ); - }); - it('has started but not yet reindexing documents', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: { - status: ReindexStatus.inProgress, - lastCompletedStep: ReindexStep.readonly, - reindexTaskPercComplete: null, - }, - warnings: [], - hasRequiredPrivileges: true, - meta: defaultReindexStatusMeta, - }); - - await openReindexFlyout(); - await proceedToReindexProgress(); - - await waitFor(() => { - expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( - 'Reindexing in progress… 5%' - ); - }); - expect(screen.queryByTestId('cancelReindexingDocumentsButton')).toBeNull(); - }); - - it('has started reindexing documents', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: { - status: ReindexStatus.inProgress, - lastCompletedStep: ReindexStep.reindexStarted, - reindexTaskPercComplete: 0.25, - }, - warnings: [], - hasRequiredPrivileges: true, - meta: defaultReindexStatusMeta, - }); - - await openReindexFlyout(); - await proceedToReindexProgress(); - - await waitFor(() => { - expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( - 'Reindexing in progress… 30%' - ); - }); - expect(await screen.findByTestId('cancelReindexingDocumentsButton')).toBeInTheDocument(); - }); - - it('has completed reindexing documents', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: { - status: ReindexStatus.inProgress, - lastCompletedStep: ReindexStep.reindexCompleted, - reindexTaskPercComplete: 1, - }, - warnings: [], - hasRequiredPrivileges: true, - meta: defaultReindexStatusMeta, - }); - - await openReindexFlyout(); - await proceedToReindexProgress(); - - await waitFor(() => { - expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( - 'Reindexing in progress… 90%' - ); - }); - expect(screen.queryByTestId('cancelReindexingDocumentsButton')).toBeNull(); - }); - - it('has completed', async () => { - httpRequestsMockHelpers.setReindexStatusResponse(MOCK_REINDEX_DEPRECATION.index!, { - reindexOp: { - status: ReindexStatus.completed, - lastCompletedStep: ReindexStep.aliasCreated, - reindexTaskPercComplete: 1, - }, - warnings: [], - hasRequiredPrivileges: true, - meta: defaultReindexStatusMeta, - }); - - await openReindexFlyout(); - await proceedToReindexProgress(); - - await waitFor(() => { - expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( - 'Reindexing in progress… 95%' - ); - }); - expect(screen.queryByTestId('cancelReindexingDocumentsButton')).toBeNull(); - }); - }); - - describe('low disk space', () => { - it('renders a warning callout if nodes detected with low disk space', async () => { - httpRequestsMockHelpers.setLoadNodeDiskSpaceResponse([ - { - nodeId: '9OFkjpAKS_aPzJAuEOSg7w', - nodeName: 'MacBook-Pro.local', - available: '25%', - }, - ]); - - const flyout = await openReindexFlyout(); - await proceedToReindexProgress(); - - expect(await within(flyout).findByTestId('lowDiskSpaceCallout')).toHaveTextContent( - 'Nodes with low disk space' - ); - expect(within(flyout).getAllByTestId('impactedNodeListItem')).toHaveLength(1); - expect(within(flyout).getAllByTestId('impactedNodeListItem')[0]).toHaveTextContent( - 'MacBook-Pro.local (25% available)' - ); - }); - }); -}); From 9aad79291882ca8ec212d6e395e78be3d2fc2a0c Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Wed, 18 Mar 2026 03:56:00 +0100 Subject: [PATCH 6/9] [Upgrade Assistant] Address CodeRabbit feedback for ES deprecations tests Tighten ES deprecations unit tests by asserting flyout body details, fixing empty-state totals, reducing selector brittleness, and eliminating shared mocks to prevent cross-test leakage. Made-with: Cursor --- .../__fixtures__/es_deprecations.ts | 54 +++++++++++-- .../cluster_settings/flyout.test.tsx | 3 + .../cluster_settings/flyout.tsx | 2 +- .../cluster_settings/table_row.tsx | 4 +- .../data_streams/flyout/container.test.tsx | 21 ++---- .../checklist/checklist_reindex_step.test.tsx | 75 ++++++++++--------- .../confirm/readonly_confirm_step.test.tsx | 52 +++---------- .../confirm/reindex_confirm_step.test.tsx | 52 +++---------- .../test_utils/confirm_step_test_scaffold.ts | 42 +++++++++++ .../resolution_table_cell.test.tsx | 48 +++++------- .../index_settings/table_row.test.tsx | 2 + .../index_settings/table_row.tsx | 4 +- .../indices/actions_table_cell.test.tsx | 53 ++----------- .../steps/warning/warning_step_modal.test.tsx | 73 +++++++++--------- .../indices/resolution_table_cell.test.tsx | 53 ++----------- .../deprecation_types/test_utils/helpers.ts | 59 +++++++++++++++ .../es_deprecations/es_deprecations.test.tsx | 4 +- .../es_deprecations_table.test.tsx | 57 +++++++++----- 18 files changed, 332 insertions(+), 326 deletions(-) create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/test_utils/confirm_step_test_scaffold.ts create mode 100644 x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/test_utils/helpers.ts diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts index 0f24e05fac0e7..f036590d7b02e 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/__fixtures__/es_deprecations.ts @@ -100,22 +100,60 @@ export const mockEsDeprecations = { } satisfies ESUpgradeStatus; export const createEsDeprecations = (numDeprecationsPerType: number): ESUpgradeStatus => { + const cloneCorrectiveAction = ( + correctiveAction: EnrichedDeprecationInfo['correctiveAction'] + ): EnrichedDeprecationInfo['correctiveAction'] => { + if (!correctiveAction) { + return undefined; + } + + switch (correctiveAction.type) { + case 'mlSnapshot': + return { ...correctiveAction }; + case 'indexSetting': + return { + ...correctiveAction, + deprecatedSettings: [...correctiveAction.deprecatedSettings], + }; + case 'clusterSetting': + return { + ...correctiveAction, + deprecatedSettings: [...correctiveAction.deprecatedSettings], + }; + case 'reindex': + return { + ...correctiveAction, + metadata: { ...correctiveAction.metadata }, + }; + default: + return { ...correctiveAction }; + } + }; + + const cloneDeprecation = (deprecation: EnrichedDeprecationInfo): EnrichedDeprecationInfo => ({ + ...deprecation, + correctiveAction: cloneCorrectiveAction(deprecation.correctiveAction), + }); + const repeat = (deprecation: EnrichedDeprecationInfo) => - Array.from({ length: numDeprecationsPerType }, () => deprecation); + Array.from({ length: numDeprecationsPerType }, () => cloneDeprecation(deprecation)); const mlDeprecations = repeat(mockMlDeprecation); const indexSettingsDeprecations = repeat(mockIndexSettingDeprecation); + const clusterSettingsDeprecations = repeat(mockClusterSettingDeprecation); const reindexDeprecations = repeat(mockReindexDeprecation); const defaultDeprecations = repeat(mockDefaultDeprecation); + const migrationsDeprecations = [ + ...defaultDeprecations, + ...reindexDeprecations, + ...indexSettingsDeprecations, + ...clusterSettingsDeprecations, + ...mlDeprecations, + ]; return { - totalCriticalDeprecations: mlDeprecations.length + reindexDeprecations.length, - migrationsDeprecations: [ - ...defaultDeprecations, - ...reindexDeprecations, - ...indexSettingsDeprecations, - ...mlDeprecations, - ], + totalCriticalDeprecations: migrationsDeprecations.filter((d) => d.level === 'critical').length, + migrationsDeprecations, totalCriticalHealthIssues: mockEsDeprecations.totalCriticalHealthIssues, enrichedHealthIndicators: mockEsDeprecations.enrichedHealthIndicators, }; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx index d3e276d108136..224f3811b2dbd 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx @@ -43,6 +43,9 @@ describe('RemoveClusterSettingsFlyout', () => { expect(screen.getByTestId('flyoutTitle')).toHaveTextContent( mockClusterSettingDeprecation.message ); + expect(screen.getByTestId('flyoutBody')).toHaveTextContent( + mockClusterSettingDeprecation.details + ); expect(screen.getByTestId('documentationLink')).toHaveAttribute( 'href', mockClusterSettingDeprecation.url diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.tsx index 53e76f3ed20b5..ce039e0733d60 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.tsx @@ -112,7 +112,7 @@ export const RemoveClusterSettingsFlyout = ({

{message}

- + {statusType === 'error' && ( <> = ({ statusType: error ? 'error' : 'complete', details: error ?? undefined, }); - closeFlyout(); + if (!error) { + closeFlyout(); + } }, [api, closeFlyout] ); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx index 782138402ed7a..3d2baa3eaaa09 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx @@ -9,8 +9,6 @@ import React from 'react'; import '@testing-library/jest-dom'; import { screen } from '@testing-library/react'; import { renderWithI18n } from '@kbn/test-jest-helpers'; -import moment from 'moment'; -import numeral from '@elastic/numeral'; import type { EnrichedDeprecationInfo } from '../../../../../../../common/types'; import { DataStreamMigrationStatus } from '../../../../../../../common/types'; @@ -56,9 +54,6 @@ jest.mock('../../../common/initializing_step', () => ({ InitializingStep: () =>
, })); -const DATE_FORMAT = 'dddd, MMMM Do YYYY, h:mm:ss a'; -const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; - const mockDeprecation: EnrichedDeprecationInfo = { type: 'data_streams', level: 'warning', @@ -101,12 +96,6 @@ const createMigrationState = (): MigrationState => ({ describe('DataStreamReindexFlyout', () => { it('renders formatted metadata in the flyout header', () => { const migrationState = createMigrationState(); - const expectedLastIndexCreationDateFormatted = moment( - migrationState.meta?.lastIndexRequiringUpgradeCreationDate - ).format(DATE_FORMAT); - const expectedDocsSizeFormatted = numeral( - migrationState.meta?.indicesRequiringUpgradeDocsSize - ).format(FILE_SIZE_DISPLAY_FORMAT); renderWithI18n( { ); expect(screen.getByTestId('flyoutTitle')).toHaveTextContent('my-data-stream'); + const lastIndexCreationDateFormatted = screen.getByTestId('confirmMigrationStep').textContent; + expect(lastIndexCreationDateFormatted).not.toBeNull(); expect(screen.getByTestId('dataStreamLastIndexCreationDate')).toHaveTextContent( - expectedLastIndexCreationDateFormatted + lastIndexCreationDateFormatted! ); - expect(screen.getByTestId('dataStreamSize')).toHaveTextContent(expectedDocsSizeFormatted); + expect(screen.getByTestId('dataStreamSize')).toHaveTextContent(/1\s?KB/); expect(screen.getByTestId('dataStreamDocumentCount')).toHaveTextContent('10'); - expect(screen.getByTestId('confirmMigrationStep')).toHaveTextContent( - expectedLastIndexCreationDateFormatted - ); + expect(screen.getByTestId('confirmMigrationStep')).toHaveTextContent(/\b20\d{2}\b/); }); }); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx index 68ec094025bbd..26542ba97a32d 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx @@ -54,20 +54,36 @@ const createMigrationState = (overrides: Partial): MigrationStat ...overrides, }); +const renderChecklistFlyout = ({ + migrationState, + executeAction = jest.fn(), + cancelAction = jest.fn(), + dataStreamName = 'my-ds', +}: { + migrationState: MigrationState; + executeAction?: () => void; + cancelAction?: () => void; + dataStreamName?: string; +}) => { + renderWithI18n( + + ); +}; + describe('ChecklistFlyoutStep (data streams)', () => { it('renders fetch-failed callout and hides main action button on fetchFailed', () => { - renderWithI18n( - - ); + renderChecklistFlyout({ + migrationState: createMigrationState({ + status: DataStreamMigrationStatus.fetchFailed, + errorMessage: 'fetch failed', + }), + }); expect(screen.getByTestId('dataStreamMigrationChecklistFlyout')).toBeInTheDocument(); expect(screen.getByTestId('fetchFailedCallout')).toHaveTextContent('true'); @@ -75,18 +91,12 @@ describe('ChecklistFlyoutStep (data streams)', () => { }); it('shows try-again button on failed status', () => { - renderWithI18n( - - ); + renderChecklistFlyout({ + migrationState: createMigrationState({ + status: DataStreamMigrationStatus.failed, + errorMessage: 'failed', + }), + }); expect(screen.getByTestId('fetchFailedCallout')).toHaveTextContent('false'); expect(screen.getByTestId('startDataStreamMigrationButton')).toHaveTextContent('Try again'); @@ -95,17 +105,12 @@ describe('ChecklistFlyoutStep (data streams)', () => { it('shows cancel button when migration is in progress', () => { const cancelAction = jest.fn(); - renderWithI18n( - - ); + renderChecklistFlyout({ + migrationState: createMigrationState({ + status: DataStreamMigrationStatus.inProgress, + }), + cancelAction, + }); fireEvent.click(screen.getByTestId('cancelDataStreamMigrationButton')); expect(cancelAction).toHaveBeenCalledTimes(1); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx index 0de2c35a63d60..1f6dbbeebebbf 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx @@ -10,24 +10,13 @@ import '@testing-library/jest-dom'; import { fireEvent, screen } from '@testing-library/react'; import { renderWithI18n } from '@kbn/test-jest-helpers'; -import type { - DataStreamMigrationWarning, - DataStreamMetadata, -} from '../../../../../../../../../common/types'; import type { WarningCheckboxProps } from './warnings'; import { ConfirmMigrationReadonlyFlyoutStep } from './readonly_confirm_step'; - -const mockLoadNodeDiskSpace = jest.fn< - { - data: Array<{ - nodeName: string; - availableBytes: number; - lowDiskSpace: boolean; - shards: string[]; - }>; - }, - [] ->(); +import { + createWarnings, + mockLoadNodeDiskSpace, + mockMeta, +} from './test_utils/confirm_step_test_scaffold'; jest.mock('../../../../../../../app_context', () => ({ useAppContext: () => ({ @@ -59,7 +48,7 @@ jest.mock('./warnings', () => ({ checked={isChecked} onChange={onChange} id={id} - data-testid={id} + data-test-subj={id} aria-label={id} /> ), @@ -69,7 +58,7 @@ jest.mock('./warnings', () => ({ checked={isChecked} onChange={onChange} id={id} - data-testid={id} + data-test-subj={id} aria-label={id} /> ), @@ -79,22 +68,7 @@ jest.mock('../../../../../common/nodes_low_disk_space', () => ({ NodesLowSpaceCallOut: () =>
, })); -const mockMeta: DataStreamMetadata = { - dataStreamName: 'my-data-stream', - documentationUrl: 'https://example.invalid/meta-docs', - lastIndexRequiringUpgradeCreationDate: 1700000000000, - allIndices: ['.ds-my-data-stream-000001', '.ds-my-data-stream-000002'], - allIndicesCount: 2, - indicesRequiringUpgradeCount: 1, - indicesRequiringUpgrade: ['.ds-my-data-stream-000001'], - indicesRequiringUpgradeDocsCount: 10, - indicesRequiringUpgradeDocsSize: 1024, -}; - -const mockWarnings: DataStreamMigrationWarning[] = [ - { warningType: 'incompatibleDataStream', meta: {}, resolutionType: 'readonly' }, - { warningType: 'affectExistingSetups', meta: {}, resolutionType: 'readonly' }, -]; +const mockWarnings = createWarnings('readonly'); describe('ConfirmMigrationReadonlyFlyoutStep', () => { beforeEach(() => { @@ -105,7 +79,7 @@ describe('ConfirmMigrationReadonlyFlyoutStep', () => { it('blocks start until all warning checkboxes are checked', () => { const startAction = jest.fn(); - const { container } = renderWithI18n( + renderWithI18n( { const startButton = screen.getByTestId('startActionButton'); expect(startButton).toBeDisabled(); - const checkboxes = container.querySelectorAll( - 'input[type="checkbox"][data-testid^="migrationWarning-"]' - ); - expect(checkboxes.length).toBe(2); - checkboxes.forEach((checkbox) => fireEvent.click(checkbox)); + const checkboxes = screen.getAllByTestId(/^migrationWarning-\d+$/); + expect(checkboxes).toHaveLength(2); + checkboxes.forEach((checkboxEl) => fireEvent.click(checkboxEl)); expect(startButton).not.toBeDisabled(); fireEvent.click(startButton); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx index 5317647fec65b..95df93f4ed01a 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx @@ -10,24 +10,13 @@ import '@testing-library/jest-dom'; import { fireEvent, screen } from '@testing-library/react'; import { renderWithI18n } from '@kbn/test-jest-helpers'; -import type { - DataStreamMigrationWarning, - DataStreamMetadata, -} from '../../../../../../../../../common/types'; import type { WarningCheckboxProps } from './warnings'; import { ConfirmMigrationReindexFlyoutStep } from './reindex_confirm_step'; - -const mockLoadNodeDiskSpace = jest.fn< - { - data: Array<{ - nodeName: string; - availableBytes: number; - lowDiskSpace: boolean; - shards: string[]; - }>; - }, - [] ->(); +import { + createWarnings, + mockLoadNodeDiskSpace, + mockMeta, +} from './test_utils/confirm_step_test_scaffold'; jest.mock('../../../../../../../app_context', () => ({ useAppContext: () => ({ @@ -59,7 +48,7 @@ jest.mock('./warnings', () => ({ checked={isChecked} onChange={onChange} id={id} - data-testid={id} + data-test-subj={id} aria-label={id} /> ), @@ -69,7 +58,7 @@ jest.mock('./warnings', () => ({ checked={isChecked} onChange={onChange} id={id} - data-testid={id} + data-test-subj={id} aria-label={id} /> ), @@ -79,22 +68,7 @@ jest.mock('../../../../../common/nodes_low_disk_space', () => ({ NodesLowSpaceCallOut: () =>
, })); -const mockMeta: DataStreamMetadata = { - dataStreamName: 'my-data-stream', - documentationUrl: 'https://example.invalid/meta-docs', - lastIndexRequiringUpgradeCreationDate: 1700000000000, - allIndices: ['.ds-my-data-stream-000001', '.ds-my-data-stream-000002'], - allIndicesCount: 2, - indicesRequiringUpgradeCount: 1, - indicesRequiringUpgrade: ['.ds-my-data-stream-000001'], - indicesRequiringUpgradeDocsCount: 10, - indicesRequiringUpgradeDocsSize: 1024, -}; - -const mockWarnings: DataStreamMigrationWarning[] = [ - { warningType: 'incompatibleDataStream', meta: {}, resolutionType: 'reindex' }, - { warningType: 'affectExistingSetups', meta: {}, resolutionType: 'reindex' }, -]; +const mockWarnings = createWarnings('reindex'); describe('ConfirmMigrationReindexFlyoutStep', () => { beforeEach(() => { @@ -105,7 +79,7 @@ describe('ConfirmMigrationReindexFlyoutStep', () => { it('blocks start until all warning checkboxes are checked', () => { const startAction = jest.fn(); - const { container } = renderWithI18n( + renderWithI18n( { const startButton = screen.getByTestId('startActionButton'); expect(startButton).toBeDisabled(); - const checkboxes = container.querySelectorAll( - 'input[type="checkbox"][data-testid^="migrationWarning-"]' - ); - expect(checkboxes.length).toBe(2); - checkboxes.forEach((checkbox) => fireEvent.click(checkbox)); + const checkboxes = screen.getAllByTestId(/^migrationWarning-\d+$/); + expect(checkboxes).toHaveLength(2); + checkboxes.forEach((checkboxEl) => fireEvent.click(checkboxEl)); expect(startButton).not.toBeDisabled(); fireEvent.click(startButton); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/test_utils/confirm_step_test_scaffold.ts b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/test_utils/confirm_step_test_scaffold.ts new file mode 100644 index 0000000000000..65302cf33d22f --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/test_utils/confirm_step_test_scaffold.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + DataStreamMetadata, + DataStreamMigrationWarning, +} from '../../../../../../../../../../common/types'; + +export const mockLoadNodeDiskSpace = jest.fn< + { + data: Array<{ + nodeName: string; + availableBytes: number; + lowDiskSpace: boolean; + shards: string[]; + }>; + }, + [] +>(); + +export const mockMeta: DataStreamMetadata = { + dataStreamName: 'my-data-stream', + documentationUrl: 'https://example.invalid/meta-docs', + lastIndexRequiringUpgradeCreationDate: 1700000000000, + allIndices: ['.ds-my-data-stream-000001', '.ds-my-data-stream-000002'], + allIndicesCount: 2, + indicesRequiringUpgradeCount: 1, + indicesRequiringUpgrade: ['.ds-my-data-stream-000001'], + indicesRequiringUpgradeDocsCount: 10, + indicesRequiringUpgradeDocsSize: 1024, +}; + +export const createWarnings = ( + resolutionType: 'readonly' | 'reindex' +): DataStreamMigrationWarning[] => [ + { warningType: 'incompatibleDataStream', meta: {}, resolutionType }, + { warningType: 'affectExistingSetups', meta: {}, resolutionType }, +]; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx index 213e3ee5a6d4d..bd5c17fe2f9a1 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/resolution_table_cell.test.tsx @@ -39,22 +39,24 @@ describe('DataStreamReindexResolutionCell', () => { mockUseDataStreamMigrationContext.mockReset(); }); + const makeDefaultMigrationContextMock = (): MigrationStateContext => ({ + loadDataStreamMetadata: jest.fn, []>(), + initMigration: jest.fn(), + startReindex: jest.fn, []>(), + cancelReindex: jest.fn, []>(), + startReadonly: jest.fn, []>(), + cancelReadonly: jest.fn, []>(), + migrationState: { + loadingState: LoadingState.Success, + status: DataStreamMigrationStatus.notStarted, + taskPercComplete: null, + errorMessage: null, + meta: null, + }, + }); + it('recommends set to read-only by default', () => { - mockUseDataStreamMigrationContext.mockReturnValue({ - loadDataStreamMetadata: jest.fn, []>(), - initMigration: jest.fn(), - startReindex: jest.fn, []>(), - cancelReindex: jest.fn, []>(), - startReadonly: jest.fn, []>(), - cancelReadonly: jest.fn, []>(), - migrationState: { - loadingState: LoadingState.Success, - status: DataStreamMigrationStatus.notStarted, - taskPercComplete: null, - errorMessage: null, - meta: null, - }, - }); + mockUseDataStreamMigrationContext.mockReturnValue(makeDefaultMigrationContextMock()); renderWithI18n(); @@ -70,21 +72,7 @@ describe('DataStreamReindexResolutionCell', () => { }, }; - mockUseDataStreamMigrationContext.mockReturnValue({ - loadDataStreamMetadata: jest.fn, []>(), - initMigration: jest.fn(), - startReindex: jest.fn, []>(), - cancelReindex: jest.fn, []>(), - startReadonly: jest.fn, []>(), - cancelReadonly: jest.fn, []>(), - migrationState: { - loadingState: LoadingState.Success, - status: DataStreamMigrationStatus.notStarted, - taskPercComplete: null, - errorMessage: null, - meta: null, - }, - }); + mockUseDataStreamMigrationContext.mockReturnValue(makeDefaultMigrationContextMock()); renderWithI18n(); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx index d5c0088b68468..75871a5fc3c01 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx @@ -136,6 +136,8 @@ describe('IndexSettingsTableRow', () => { await removeIndexSettings('my_index', ['index.routing.allocation.include._tier']); }); + expect(mockRemoveContent).not.toHaveBeenCalled(); + const resolutionCell = within( screen.getByTestId('indexSettingsTableCell-correctiveAction') ).getByTestId('indexSettingsResolutionStatusCell'); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx index 2ae389c32df42..9177c7342560f 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.tsx @@ -58,7 +58,9 @@ export const IndexSettingsTableRow: React.FunctionComponent = ({ statusType: error ? 'error' : 'complete', details: error ?? undefined, }); - closeFlyout(); + if (!error) { + closeFlyout(); + } }, [api, closeFlyout] ); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx index ead98c5f4d299..875b64351d74b 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/actions_table_cell.test.tsx @@ -17,10 +17,12 @@ import type { UnfreezeAction, } from '../../../../../../common/types'; import type { IndexStateContext } from './context'; -import type { ReindexState } from './use_reindex'; -import type { UpdateIndexState } from './use_update_index'; import { ReindexActionCell } from './actions_table_cell'; -import { LoadingState } from '../../../types'; +import { + createIndexContext, + createReindexState, + createUpdateIndexState, +} from '../test_utils/helpers'; const mockUseIndexContext = jest.fn(); @@ -28,51 +30,6 @@ jest.mock('./context', () => ({ useIndexContext: () => mockUseIndexContext(), })); -const createReindexState = (overrides?: Partial): ReindexState => ({ - loadingState: LoadingState.Success, - errorMessage: null, - reindexTaskPercComplete: null, - status: undefined, - lastCompletedStep: undefined, - cancelLoadingState: undefined, - reindexWarnings: undefined, - hasRequiredPrivileges: true, - meta: { - indexName: 'test-index', - reindexName: 'test-index-reindexed', - aliases: [], - isFrozen: false, - isReadonly: false, - isInDataStream: false, - isClosedIndex: false, - isFollowerIndex: false, - }, - ...overrides, -}); - -const createUpdateIndexState = (overrides?: Partial): UpdateIndexState => ({ - failedBefore: false, - status: 'incomplete', - ...overrides, -}); - -const createIndexContext = ({ - deprecation, - reindexState, - updateIndexState, -}: { - deprecation: EnrichedDeprecationInfo; - reindexState: ReindexState; - updateIndexState: UpdateIndexState; -}): IndexStateContext => ({ - deprecation, - reindexState, - updateIndexState, - startReindex: jest.fn, []>(), - cancelReindex: jest.fn, []>(), - updateIndex: jest.fn, []>(), -}); - const baseDeprecation = { level: 'critical', resolveDuringUpgrade: false, diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx index 898fc359b4833..3b6ca5df58d92 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx @@ -38,7 +38,9 @@ jest.mock('./warning_step_checkbox', () => ({ isChecked: boolean; onChange: (event: React.ChangeEvent) => void; id: string; - }) => , + }) => ( + + ), ReplaceIndexWithAliasWarningCheckbox: ({ isChecked, onChange, @@ -47,7 +49,9 @@ jest.mock('./warning_step_checkbox', () => ({ isChecked: boolean; onChange: (event: React.ChangeEvent) => void; id: string; - }) => , + }) => ( + + ), MakeIndexReadonlyWarningCheckbox: ({ isChecked, onChange, @@ -56,20 +60,22 @@ jest.mock('./warning_step_checkbox', () => ({ isChecked: boolean; onChange: (event: React.ChangeEvent) => void; id: string; - }) => , + }) => ( + + ), })); jest.mock('../callouts', () => ({ - FollowerIndexCallout: () =>
, - ESTransformsTargetCallout: () =>
, - MlAnomalyCallout: () =>
, + FollowerIndexCallout: () =>
, + ESTransformsTargetCallout: () =>
, + MlAnomalyCallout: () =>
, FetchFailedCallOut: ({ errorMessage }: { errorMessage: string }) => ( -
{errorMessage}
+
{errorMessage}
), })); jest.mock('../../../../../common/nodes_low_disk_space', () => ({ - NodesLowSpaceCallOut: () =>
, + NodesLowSpaceCallOut: () =>
, })); const mockReindexState = { @@ -103,7 +109,7 @@ const mockWarnings: IndexWarning[] = [ { warningType: 'replaceIndexWithAlias', meta: {}, flow: 'readonly' }, ]; -const baseProps = { +const getBaseProps = () => ({ closeModal: jest.fn(), confirm: jest.fn(), meta: { @@ -119,69 +125,60 @@ const baseProps = { deprecation: mockDeprecation, reindexState: mockReindexState, flow: 'readonly' as const, -}; +}); describe('WarningModalStep', () => { it('renders read-only modal and disables continue until all checkboxes are checked', () => { - const { container } = renderWithI18n(); + const props = getBaseProps(); + renderWithI18n(); expect(screen.getByText(/Set index to read-only/i)).toBeInTheDocument(); expect(screen.getByText(/Old indices can maintain compatibility/i)).toBeInTheDocument(); const continueBtn = screen.getByRole('button', { name: /Set to read-only/i }); expect(continueBtn).toBeDisabled(); // Check all checkboxes - const checkboxes = container.querySelectorAll( - 'input[type="checkbox"][data-testid^="reindexWarning-"]' - ); - checkboxes.forEach((checkbox) => { - fireEvent.click(checkbox); + const checkboxes = screen.getAllByTestId(/^reindexWarning-\d+$/); + checkboxes.forEach((checkboxEl) => { + fireEvent.click(checkboxEl); }); expect(continueBtn).not.toBeDisabled(); }); it('calls confirm when continue is clicked in read-only flow', () => { - const { container } = renderWithI18n(); - const checkboxes = container.querySelectorAll( - 'input[type="checkbox"][data-testid^="reindexWarning-"]' - ); - checkboxes.forEach((checkbox) => { - fireEvent.click(checkbox); + const props = getBaseProps(); + renderWithI18n(); + const checkboxes = screen.getAllByTestId(/^reindexWarning-\d+$/); + checkboxes.forEach((checkboxEl) => { + fireEvent.click(checkboxEl); }); const continueBtn = screen.getByRole('button', { name: /Set to read-only/i }); fireEvent.click(continueBtn); - expect(baseProps.confirm).toHaveBeenCalled(); + expect(props.confirm).toHaveBeenCalledTimes(1); }); it('renders unfreeze modal with correct title and button', () => { + const props = getBaseProps(); renderWithI18n( - + ); expect(screen.getByTestId('updateIndexModalTitle')).toHaveTextContent(/Unfreeze index/i); expect(screen.getByRole('button', { name: /Unfreeze index/i })).toBeInTheDocument(); }); it('calls confirm when continue is clicked in unfreeze flow', () => { + const props = getBaseProps(); renderWithI18n( - + ); const continueBtn = screen.getByRole('button', { name: /Unfreeze index/i }); fireEvent.click(continueBtn); - expect(baseProps.confirm).toHaveBeenCalled(); + expect(props.confirm).toHaveBeenCalledTimes(1); }); it('calls closeModal when cancel is clicked', () => { - renderWithI18n(); + const props = getBaseProps(); + renderWithI18n(); const cancelBtn = screen.getByRole('button', { name: /Cancel/i }); fireEvent.click(cancelBtn); - expect(baseProps.closeModal).toHaveBeenCalled(); + expect(props.closeModal).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx index 6695ca87115db..2ba018e26db67 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx @@ -17,10 +17,12 @@ import type { UnfreezeAction, } from '../../../../../../common/types'; import type { IndexStateContext } from './context'; -import type { ReindexState } from './use_reindex'; -import type { UpdateIndexState } from './use_update_index'; import { ReindexResolutionCell } from './resolution_table_cell'; -import { LoadingState } from '../../../types'; +import { + createIndexContext, + createReindexState, + createUpdateIndexState, +} from '../test_utils/helpers'; const mockUseIndexContext = jest.fn(); @@ -28,51 +30,6 @@ jest.mock('./context', () => ({ useIndexContext: () => mockUseIndexContext(), })); -const createReindexState = (overrides?: Partial): ReindexState => ({ - loadingState: LoadingState.Success, - errorMessage: null, - reindexTaskPercComplete: null, - status: undefined, - lastCompletedStep: undefined, - cancelLoadingState: undefined, - reindexWarnings: undefined, - hasRequiredPrivileges: true, - meta: { - indexName: 'test-index', - reindexName: 'test-index-reindexed', - aliases: [], - isFrozen: false, - isReadonly: false, - isInDataStream: false, - isClosedIndex: false, - isFollowerIndex: false, - }, - ...overrides, -}); - -const createUpdateIndexState = (overrides?: Partial): UpdateIndexState => ({ - failedBefore: false, - status: 'incomplete', - ...overrides, -}); - -const createIndexContext = ({ - deprecation, - reindexState, - updateIndexState, -}: { - deprecation: EnrichedDeprecationInfo; - reindexState: ReindexState; - updateIndexState: UpdateIndexState; -}): IndexStateContext => ({ - deprecation, - reindexState, - updateIndexState, - startReindex: jest.fn, []>(), - cancelReindex: jest.fn, []>(), - updateIndex: jest.fn, []>(), -}); - const baseDeprecation = { level: 'critical', resolveDuringUpgrade: false, diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/test_utils/helpers.ts b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/test_utils/helpers.ts new file mode 100644 index 0000000000000..8f4f7fe60ffce --- /dev/null +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/test_utils/helpers.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import type { IndexStateContext } from '../indices/context'; +import type { ReindexState } from '../indices/use_reindex'; +import type { UpdateIndexState } from '../indices/use_update_index'; +import { LoadingState } from '../../../types'; + +export const createReindexState = (overrides?: Partial): ReindexState => ({ + loadingState: LoadingState.Success, + errorMessage: null, + reindexTaskPercComplete: null, + status: undefined, + lastCompletedStep: undefined, + cancelLoadingState: undefined, + reindexWarnings: undefined, + hasRequiredPrivileges: true, + meta: { + indexName: 'test-index', + reindexName: 'test-index-reindexed', + aliases: [], + isFrozen: false, + isReadonly: false, + isInDataStream: false, + isClosedIndex: false, + isFollowerIndex: false, + }, + ...overrides, +}); + +export const createUpdateIndexState = ( + overrides?: Partial +): UpdateIndexState => ({ + failedBefore: false, + status: 'incomplete', + ...overrides, +}); + +export const createIndexContext = ({ + deprecation, + reindexState, + updateIndexState, +}: { + deprecation: EnrichedDeprecationInfo; + reindexState: ReindexState; + updateIndexState: UpdateIndexState; +}): IndexStateContext => ({ + deprecation, + reindexState, + updateIndexState, + startReindex: jest.fn, []>(), + cancelReindex: jest.fn, []>(), + updateIndex: jest.fn, []>(), +}); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx index 1a68ef907d609..c08ded8e75c7b 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx @@ -91,7 +91,7 @@ describe('EsDeprecations', () => { it('shows no deprecations prompt', async () => { mockUseLoadEsDeprecations.mockReturnValue({ - data: { ...mockEsDeprecations, migrationsDeprecations: [] }, + data: { ...mockEsDeprecations, migrationsDeprecations: [], totalCriticalDeprecations: 0 }, isLoading: false, error: undefined, resendRequest: jest.fn(), @@ -201,7 +201,7 @@ describe('EsDeprecations', () => { renderPage(); expect(await screen.findByTestId('deprecationsPageLoadingError')).toHaveTextContent( - 'Upgrade Kibana to the same version as your Elasticsearch cluster. One or more nodes in the cluster is running a different version than Kibana.' + /Upgrade Kibana to the same version as your Elasticsearch cluster/i ); }); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx index e79a89a5f0cad..93301c3245069 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx @@ -62,8 +62,25 @@ describe('EsDeprecationsTable', () => { }; const getPaginationItemsCount = () => { - const pagination = screen.getByTestId('esDeprecationsPagination'); - return pagination.querySelectorAll('.euiPagination__item').length; + return within(screen.getByTestId('esDeprecationsPagination')).getAllByTestId( + /^pagination-button-\d+$/ + ).length; + }; + + const openStatusFilter = async (user: ReturnType) => { + await user.click( + within(screen.getByTestId('searchBarContainer')).getByRole('button', { name: /Status/i }) + ); + }; + + const openTypeFilter = async (user: ReturnType) => { + await user.click( + within(screen.getByTestId('searchBarContainer')).getByRole('button', { name: /Type/i }) + ); + }; + + const clickFilterOption = async (user: ReturnType, label: string) => { + await user.click(await screen.findByText(label)); }; it('calls reload when refresh is clicked', async () => { @@ -120,25 +137,31 @@ describe('EsDeprecationsTable', () => { await renderTable(migrationsDeprecations); - const searchBar = screen.getByTestId('searchBarContainer'); - const filterButtons = searchBar.querySelectorAll('button.euiFilterButton'); - await user.click(filterButtons[0]); + await openStatusFilter(user); + await clickFilterOption(user, 'Critical'); - const criticalFilterButton = await waitFor(() => { - const el = document.body.querySelector('.euiSelectableListItem[title="Critical"]'); - expect(el).not.toBeNull(); - return el; + await waitFor(() => { + expect(getPaginationItemsCount()).toEqual(Math.ceil(criticalDeprecations.length / 50)); + expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( + Math.min(criticalDeprecations.length, 50) + ); }); + }); + + it('updates pagination when type filter changes', async () => { + const user = userEvent.setup(); + const { migrationsDeprecations } = createEsDeprecations(20); + const mlDeprecations = migrationsDeprecations.filter((d) => d.type === 'ml_settings'); + + await renderTable(migrationsDeprecations); - expect(criticalFilterButton).not.toBeNull(); - if (criticalFilterButton) { - await user.click(criticalFilterButton); - } + await openTypeFilter(user); + await clickFilterOption(user, 'Machine Learning'); await waitFor(() => { - expect(getPaginationItemsCount()).toEqual(Math.ceil(criticalDeprecations.length / 50)); + expect(getPaginationItemsCount()).toEqual(Math.ceil(mlDeprecations.length / 50)); expect(screen.getAllByTestId('deprecationTableRow')).toHaveLength( - Math.min(criticalDeprecations.length, 50) + Math.min(mlDeprecations.length, 50) ); }); }); @@ -171,9 +194,7 @@ describe('EsDeprecationsTable', () => { const getFirstRowMessageCellText = () => { const row = screen.getAllByTestId('deprecationTableRow')[0]; - const messageCell = row.querySelector('[data-test-subj$="-message"]'); - expect(messageCell).not.toBeNull(); - return messageCell?.textContent; + return within(row).getByTestId(/-message$/).textContent; }; expect(getPaginationItemsCount()).toBeGreaterThan(1); From ed7a74c46a6b9ba7b9873b819ee8fe84da87c489 Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Wed, 18 Mar 2026 12:46:18 +0100 Subject: [PATCH 7/9] [Upgrade Assistant] Tighten ES deprecations unit coverage parity Add missing unit assertions and scenarios to fully cover behaviors previously exercised by the removed client integration suite. Made-with: Cursor --- .../checklist/checklist_reindex_step.test.tsx | 1 + .../flyout/steps/checklist/progress.test.tsx | 6 + .../flyout/steps/reindex/progress.test.tsx | 151 +++++++++++++++--- .../indices/resolution_table_cell.test.tsx | 14 ++ .../ml_snapshots/flyout.test.tsx | 20 +++ 5 files changed, 166 insertions(+), 26 deletions(-) diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx index 26542ba97a32d..f9f382f01a624 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx @@ -112,6 +112,7 @@ describe('ChecklistFlyoutStep (data streams)', () => { cancelAction, }); + expect(screen.getByTestId('startDataStreamMigrationButton')).toBeDisabled(); fireEvent.click(screen.getByTestId('cancelDataStreamMigrationButton')); expect(cancelAction).toHaveBeenCalledTimes(1); }); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx index aea668b2b4cd5..8fcbedabc5d2b 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/progress.test.tsx @@ -43,6 +43,9 @@ describe('MigrationProgress', () => { renderWithI18n(); expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent('logs-foo'); + expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( + 'Reindexing logs-foo in progress…' + ); expect(screen.getByText('1 Index failed to get reindexed.')).toBeInTheDocument(); expect(screen.getByText('2 Indices successfully reindexed.')).toBeInTheDocument(); expect(screen.getByText('3 Indices currently getting reindexed.')).toBeInTheDocument(); @@ -70,6 +73,9 @@ describe('MigrationProgress', () => { renderWithI18n(); + expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( + 'Setting to read-only logs-bar in progress…' + ); expect(screen.getByText('1 Index successfully set to read-only.')).toBeInTheDocument(); expect(screen.getByText('0 Indices currently getting set to read-only.')).toBeInTheDocument(); expect(screen.getByText('1 Index waiting to start.')).toBeInTheDocument(); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/progress.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/progress.test.tsx index 12686fb677181..d01281e004c21 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/progress.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/progress.test.tsx @@ -13,32 +13,28 @@ import { renderWithI18n } from '@kbn/test-jest-helpers'; import { ReindexStep } from '@kbn/reindex-service-plugin/common'; import { ReindexStatus } from '@kbn/upgrade-assistant-pkg-common'; import { LoadingState } from '../../../../../../types'; -import type { ReindexState } from '../../../use_reindex'; import { ReindexProgress } from './progress'; +import { createReindexState } from '../../../../test_utils/helpers'; describe('ReindexProgress', () => { it('renders', () => { renderWithI18n( ); @@ -61,25 +57,22 @@ describe('ReindexProgress', () => { it('displays errors in the step that failed', () => { renderWithI18n( ); @@ -97,4 +90,110 @@ describe('ReindexProgress', () => { expect(stepContent!).toHaveTextContent('There was an error'); expect(stepContent!).toHaveTextContent('This is an error that happened on alias switch'); }); + + it('has started but not yet reindexing documents', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( + 'Reindexing in progress… 5%' + ); + expect(screen.queryByTestId('cancelReindexingDocumentsButton')).toBeNull(); + }); + + it('has started reindexing documents', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( + 'Reindexing in progress… 30%' + ); + expect(screen.getByTestId('cancelReindexingDocumentsButton')).toBeInTheDocument(); + }); + + it('has completed reindexing documents', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent( + 'Reindexing in progress… 90%' + ); + expect(screen.queryByTestId('cancelReindexingDocumentsButton')).toBeNull(); + }); + + it('has completed', () => { + renderWithI18n( + + ); + + expect(screen.getByTestId('reindexChecklistTitle')).toHaveTextContent('Reindexing process'); + expect(screen.queryByTestId('cancelReindexingDocumentsButton')).toBeNull(); + }); }); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx index 2ba018e26db67..e8d7af3f18f6a 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/resolution_table_cell.test.tsx @@ -69,6 +69,20 @@ describe('ReindexResolutionCell', () => { mockUseIndexContext.mockReset(); }); + it('recommends reindexing by default', () => { + mockUseIndexContext.mockReturnValue( + createIndexContext({ + deprecation: reindexDeprecation, + reindexState: createReindexState(), + updateIndexState: createUpdateIndexState(), + }) + ); + + renderWithI18n(); + + expect(screen.getByText('Recommended: reindex')).toBeInTheDocument(); + }); + it('recommends unfreeze for frozen indices', () => { mockUseIndexContext.mockReturnValue( createIndexContext({ diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx index 93502e2a6c6af..67573a9a5b303 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx @@ -129,6 +129,26 @@ describe('FixSnapshotsFlyout', () => { expect(screen.getByTestId('upgradeSnapshotButton')).toHaveTextContent('Retry upgrade'); }); + it('shows error callout and changes action labels on delete failure', () => { + const error: ResponseError = { + statusCode: 500, + message: 'Delete snapshot error', + }; + const snapshotState: SnapshotState = { + jobId: MOCK_JOB_ID, + snapshotId: MOCK_SNAPSHOT_ID, + status: 'error', + action: 'delete', + error, + }; + + renderFlyout({ snapshotState, mlUpgradeModeEnabled: false }); + + expect(screen.getByTestId('resolveSnapshotError')).toHaveTextContent('Error deleting snapshot'); + expect(screen.getByTestId('resolveSnapshotError')).toHaveTextContent('Delete snapshot error'); + expect(screen.getByTestId('deleteSnapshotButton')).toHaveTextContent('Retry delete'); + }); + it('calls upgradeSnapshot and closes flyout', () => { const snapshotState: SnapshotState = { jobId: MOCK_JOB_ID, From 89d8e4044505b305c2f0532405a5d73fcffa7568 Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Thu, 19 Mar 2026 00:45:01 +0100 Subject: [PATCH 8/9] [Upgrade Assistant] Make ES deprecations test mocks resilient Prefer partial mocks (merge jest.requireActual) for shared modules like app context, shared imports, and ui metrics to avoid brittle failures when new exports are added. Made-with: Cursor --- .../cluster_settings/flyout.test.tsx | 17 ++++-- .../cluster_settings/table_row.test.tsx | 46 +++++++++------ .../data_streams/flyout/container.test.tsx | 29 ++++++---- .../checklist/checklist_reindex_step.test.tsx | 21 ++++--- .../confirm/readonly_confirm_step.test.tsx | 33 ++++++----- .../confirm/reindex_confirm_step.test.tsx | 33 ++++++----- .../default/table_row.test.tsx | 23 +++++--- .../index_settings/flyout.test.tsx | 17 ++++-- .../index_settings/table_row.test.tsx | 46 +++++++++------ .../steps/reindex/reindex_step.test.tsx | 2 + .../steps/warning/warning_step.test.tsx | 2 + .../steps/warning/warning_step_modal.test.tsx | 27 +++++---- .../ml_snapshots/flyout.test.tsx | 45 +++++++++------ .../es_deprecations/es_deprecations.test.tsx | 57 ++++++++++--------- .../es_deprecations_table.test.tsx | 31 +++++----- 15 files changed, 260 insertions(+), 169 deletions(-) diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx index 224f3811b2dbd..6e1fde4dc5e41 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/flyout.test.tsx @@ -14,12 +14,17 @@ import type { ResponseError } from '../../../../../../common/types'; import { mockClusterSettingDeprecation } from '../../__fixtures__/es_deprecations'; import { RemoveClusterSettingsFlyout } from './flyout'; -jest.mock('../../../../lib/ui_metric', () => ({ - uiMetricService: { - trackUiMetric: jest.fn(), - }, - UIM_CLUSTER_SETTINGS_DELETE_CLICK: 'UIM_CLUSTER_SETTINGS_DELETE_CLICK', -})); +jest.mock('../../../../lib/ui_metric', () => { + const actual = jest.requireActual('../../../../lib/ui_metric'); + + return { + ...actual, + uiMetricService: { + ...actual.uiMetricService, + trackUiMetric: jest.fn(), + }, + }; +}); describe('RemoveClusterSettingsFlyout', () => { const closeFlyout = jest.fn(); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx index 145bf9a31b7ec..9a60b66657b53 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/cluster_settings/table_row.test.tsx @@ -36,25 +36,37 @@ const mockAddContent = jest.fn< >(); const mockRemoveContent = jest.fn(); -jest.mock('../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - api: { - updateClusterSettings: (...args: Parameters) => - mockUpdateClusterSettings(...args), +jest.mock('../../../../app_context', () => { + const actual = jest.requireActual('../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + api: { + updateClusterSettings: (...args: Parameters) => + mockUpdateClusterSettings(...args), + }, }, - }, - }), -})); - -jest.mock('../../../../../shared_imports', () => ({ - GlobalFlyout: { - useGlobalFlyout: () => ({ - addContent: (...args: Parameters) => mockAddContent(...args), - removeContent: (...args: Parameters) => mockRemoveContent(...args), }), - }, -})); + }; +}); + +jest.mock('../../../../../shared_imports', () => { + const actual = jest.requireActual('../../../../../shared_imports'); + + return { + ...actual, + GlobalFlyout: { + ...actual.GlobalFlyout, + useGlobalFlyout: () => ({ + addContent: (...args: Parameters) => mockAddContent(...args), + removeContent: (...args: Parameters) => + mockRemoveContent(...args), + }), + }, + }; +}); describe('ClusterSettingsTableRow', () => { const rowFieldNames: DeprecationTableColumns[] = ['correctiveAction', 'actions']; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx index 3d2baa3eaaa09..5d4a10d63cee2 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/container.test.tsx @@ -16,18 +16,25 @@ import { LoadingState } from '../../../../types'; import type { MigrationState } from '../use_migration_state'; import { DataStreamReindexFlyout } from './container'; -jest.mock('../../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - api: { - useLoadNodeDiskSpace: () => ({ data: [] }), - }, - core: { - docLinks: { links: { upgradeAssistant: { dataStreamReindex: 'https://example.invalid' } } }, +jest.mock('../../../../../app_context', () => { + const actual = jest.requireActual('../../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => ({ data: [] }), + }, + core: { + docLinks: { + links: { upgradeAssistant: { dataStreamReindex: 'https://example.invalid' } }, + }, + }, }, - }, - }), -})); + }), + }; +}); jest.mock('../use_migration_step', () => ({ useMigrationStep: () => ['confirm', jest.fn()] as const, diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx index f9f382f01a624..a8035bdc8f746 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/checklist/checklist_reindex_step.test.tsx @@ -15,15 +15,20 @@ import { LoadingState } from '../../../../../../types'; import type { MigrationState } from '../../../use_migration_state'; import { ChecklistFlyoutStep } from './checklist_reindex_step'; -jest.mock('../../../../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - api: { - useLoadNodeDiskSpace: () => ({ data: [] }), +jest.mock('../../../../../../../app_context', () => { + const actual = jest.requireActual('../../../../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => ({ data: [] }), + }, }, - }, - }), -})); + }), + }; +}); jest.mock('../../../../../common/nodes_low_disk_space', () => ({ NodesLowSpaceCallOut: () =>
, diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx index 1f6dbbeebebbf..fda7c94c74e59 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/readonly_confirm_step.test.tsx @@ -18,24 +18,29 @@ import { mockMeta, } from './test_utils/confirm_step_test_scaffold'; -jest.mock('../../../../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - api: { - useLoadNodeDiskSpace: () => mockLoadNodeDiskSpace(), - }, - core: { - docLinks: { - links: { - upgradeAssistant: { - dataStreamReindex: 'https://example.invalid/reindex-docs', +jest.mock('../../../../../../../app_context', () => { + const actual = jest.requireActual('../../../../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => mockLoadNodeDiskSpace(), + }, + core: { + docLinks: { + links: { + upgradeAssistant: { + dataStreamReindex: 'https://example.invalid/reindex-docs', + }, }, }, }, }, - }, - }), -})); + }), + }; +}); jest.mock('./warnings', () => ({ IncompatibleDataInDataStreamWarningCheckbox: ({ diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx index 95df93f4ed01a..a861e325794cf 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/flyout/steps/confirm/reindex_confirm_step.test.tsx @@ -18,24 +18,29 @@ import { mockMeta, } from './test_utils/confirm_step_test_scaffold'; -jest.mock('../../../../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - api: { - useLoadNodeDiskSpace: () => mockLoadNodeDiskSpace(), - }, - core: { - docLinks: { - links: { - upgradeAssistant: { - dataStreamReindex: 'https://example.invalid/reindex-docs', +jest.mock('../../../../../../../app_context', () => { + const actual = jest.requireActual('../../../../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => mockLoadNodeDiskSpace(), + }, + core: { + docLinks: { + links: { + upgradeAssistant: { + dataStreamReindex: 'https://example.invalid/reindex-docs', + }, }, }, }, }, - }, - }), -})); + }), + }; +}); jest.mock('./warnings', () => ({ IncompatibleDataInDataStreamWarningCheckbox: ({ diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx index c6c559bdf4f4d..bc52c147f764b 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/default/table_row.test.tsx @@ -17,14 +17,21 @@ import { DefaultTableRow } from './table_row'; const mockAddContent = jest.fn(); const mockRemoveContent = jest.fn(); -jest.mock('../../../../../shared_imports', () => ({ - GlobalFlyout: { - useGlobalFlyout: () => ({ - addContent: (...args: Parameters) => mockAddContent(...args), - removeContent: (...args: Parameters) => mockRemoveContent(...args), - }), - }, -})); +jest.mock('../../../../../shared_imports', () => { + const actual = jest.requireActual('../../../../../shared_imports'); + + return { + ...actual, + GlobalFlyout: { + ...actual.GlobalFlyout, + useGlobalFlyout: () => ({ + addContent: (...args: Parameters) => mockAddContent(...args), + removeContent: (...args: Parameters) => + mockRemoveContent(...args), + }), + }, + }; +}); describe('DefaultTableRow', () => { const rowFieldNames: DeprecationTableColumns[] = ['message', 'actions']; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx index db4435e645442..35b0da93bfbcc 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/flyout.test.tsx @@ -14,12 +14,17 @@ import type { ResponseError } from '../../../../../../common/types'; import { mockIndexSettingDeprecation } from '../../__fixtures__/es_deprecations'; import { RemoveIndexSettingsFlyout } from './flyout'; -jest.mock('../../../../lib/ui_metric', () => ({ - uiMetricService: { - trackUiMetric: jest.fn(), - }, - UIM_INDEX_SETTINGS_DELETE_CLICK: 'UIM_INDEX_SETTINGS_DELETE_CLICK', -})); +jest.mock('../../../../lib/ui_metric', () => { + const actual = jest.requireActual('../../../../lib/ui_metric'); + + return { + ...actual, + uiMetricService: { + ...actual.uiMetricService, + trackUiMetric: jest.fn(), + }, + }; +}); describe('RemoveIndexSettingsFlyout', () => { const closeFlyout = jest.fn(); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx index 75871a5fc3c01..e87fcb74ffed4 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/index_settings/table_row.test.tsx @@ -39,25 +39,37 @@ const mockAddContent = jest.fn< >(); const mockRemoveContent = jest.fn(); -jest.mock('../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - api: { - updateIndexSettings: (...args: Parameters) => - mockUpdateIndexSettings(...args), +jest.mock('../../../../app_context', () => { + const actual = jest.requireActual('../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + api: { + updateIndexSettings: (...args: Parameters) => + mockUpdateIndexSettings(...args), + }, }, - }, - }), -})); - -jest.mock('../../../../../shared_imports', () => ({ - GlobalFlyout: { - useGlobalFlyout: () => ({ - addContent: (...args: Parameters) => mockAddContent(...args), - removeContent: (...args: Parameters) => mockRemoveContent(...args), }), - }, -})); + }; +}); + +jest.mock('../../../../../shared_imports', () => { + const actual = jest.requireActual('../../../../../shared_imports'); + + return { + ...actual, + GlobalFlyout: { + ...actual.GlobalFlyout, + useGlobalFlyout: () => ({ + addContent: (...args: Parameters) => mockAddContent(...args), + removeContent: (...args: Parameters) => + mockRemoveContent(...args), + }), + }, + }; +}); describe('IndexSettingsTableRow', () => { const rowFieldNames: DeprecationTableColumns[] = ['correctiveAction', 'actions']; diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/reindex_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/reindex_step.test.tsx index cffd70208198a..3bd18edf5c472 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/reindex_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/reindex/reindex_step.test.tsx @@ -17,9 +17,11 @@ import { ReindexFlyoutStep } from './reindex_step'; import { renderWithI18n } from '@kbn/test-jest-helpers'; jest.mock('../../../../../../../app_context', () => { + const actual = jest.requireActual('../../../../../../../app_context'); const { docLinksServiceMock } = jest.requireActual('@kbn/core-doc-links-browser-mocks'); return { + ...actual, useAppContext: () => { return { services: { diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step.test.tsx index 9bdcd2543f27d..ff68789c85162 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step.test.tsx @@ -23,9 +23,11 @@ import { renderWithI18n } from '@kbn/test-jest-helpers'; const kibanaVersion = new SemVer('8.0.0'); jest.mock('../../../../../../../app_context', () => { + const actual = jest.requireActual('../../../../../../../app_context'); const { docLinksServiceMock } = jest.requireActual('@kbn/core-doc-links-browser-mocks'); return { + ...actual, useAppContext: () => { return { services: { diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx index 3b6ca5df58d92..c0e57ede88d6c 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/indices/flyout/steps/warning/warning_step_modal.test.tsx @@ -16,18 +16,23 @@ import { renderWithI18n } from '@kbn/test-jest-helpers'; import { WarningModalStep } from './warning_step_modal'; // Mocks -jest.mock('../../../../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - api: { - useLoadNodeDiskSpace: () => ({ data: [] }), - }, - core: { - docLinks: { links: {} }, +jest.mock('../../../../../../../app_context', () => { + const actual = jest.requireActual('../../../../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + api: { + useLoadNodeDiskSpace: () => ({ data: [] }), + }, + core: { + docLinks: { links: {} }, + }, }, - }, - }), -})); + }), + }; +}); jest.mock('./warning_step_checkbox', () => ({ DeprecatedSettingWarningCheckbox: ({ diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx index 67573a9a5b303..294c5e67289fb 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/ml_snapshots/flyout.test.tsx @@ -19,29 +19,38 @@ import { import type { SnapshotState } from './use_snapshot_state'; import { FixSnapshotsFlyout } from './flyout'; -jest.mock('../../../../app_context', () => ({ - useAppContext: () => ({ - services: { - core: { - docLinks: { - links: { - ml: { - setUpgradeMode: 'https://example.invalid/ml-upgrade-mode', +jest.mock('../../../../app_context', () => { + const actual = jest.requireActual('../../../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + services: { + core: { + docLinks: { + links: { + ml: { + setUpgradeMode: 'https://example.invalid/ml-upgrade-mode', + }, }, }, }, }, + }), + }; +}); + +jest.mock('../../../../lib/ui_metric', () => { + const actual = jest.requireActual('../../../../lib/ui_metric'); + + return { + ...actual, + uiMetricService: { + ...actual.uiMetricService, + trackUiMetric: jest.fn(), }, - }), -})); - -jest.mock('../../../../lib/ui_metric', () => ({ - uiMetricService: { - trackUiMetric: jest.fn(), - }, - UIM_ML_SNAPSHOT_UPGRADE_CLICK: 'UIM_ML_SNAPSHOT_UPGRADE_CLICK', - UIM_ML_SNAPSHOT_DELETE_CLICK: 'UIM_ML_SNAPSHOT_DELETE_CLICK', -})); + }; +}); describe('FixSnapshotsFlyout', () => { const closeFlyout = jest.fn(); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx index c08ded8e75c7b..5810c537d6c9b 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.test.tsx @@ -21,39 +21,44 @@ const mockBreadcrumbsSetBreadcrumbs = jest.fn(); const mockUseLoadEsDeprecations = jest.fn(); const mockUseLoadRemoteClusters = jest.fn(); -jest.mock('../../app_context', () => ({ - useAppContext: () => ({ - plugins: { - share: { - url: { - locators: { - get: () => ({ - useUrl: () => '/app/management/data/remote_clusters', - }), +jest.mock('../../app_context', () => { + const actual = jest.requireActual('../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + plugins: { + share: { + url: { + locators: { + get: () => ({ + useUrl: () => '/app/management/data/remote_clusters', + }), + }, }, }, }, - }, - services: { - api: { - useLoadEsDeprecations: () => mockUseLoadEsDeprecations(), - useLoadRemoteClusters: () => mockUseLoadRemoteClusters(), - }, - breadcrumbs: { - setBreadcrumbs: mockBreadcrumbsSetBreadcrumbs, - }, - core: { - docLinks: { - links: { - upgradeAssistant: { - batchReindex: 'https://example.invalid/batch-reindex', + services: { + api: { + useLoadEsDeprecations: () => mockUseLoadEsDeprecations(), + useLoadRemoteClusters: () => mockUseLoadRemoteClusters(), + }, + breadcrumbs: { + setBreadcrumbs: mockBreadcrumbsSetBreadcrumbs, + }, + core: { + docLinks: { + links: { + upgradeAssistant: { + batchReindex: 'https://example.invalid/batch-reindex', + }, }, }, }, }, - }, - }), -})); + }), + }; +}); jest.mock('./es_deprecations_table', () => ({ EsDeprecationsTable: () =>
, diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx index 93301c3245069..182a0d53c0448 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations_table.test.tsx @@ -14,20 +14,25 @@ import { renderWithI18n } from '@kbn/test-jest-helpers'; import type { EnrichedDeprecationInfo } from '../../../../common/types'; import { createEsDeprecations } from './__fixtures__/es_deprecations'; -jest.mock('../../app_context', () => ({ - useAppContext: () => ({ - kibanaVersionInfo: { - currentMajor: 8, - currentMinor: 0, - currentPatch: 0, - }, - services: { - api: { - useLoadMlUpgradeMode: () => ({ data: { mlUpgradeModeEnabled: false } }), +jest.mock('../../app_context', () => { + const actual = jest.requireActual('../../app_context'); + + return { + ...actual, + useAppContext: () => ({ + kibanaVersionInfo: { + currentMajor: 8, + currentMinor: 0, + currentPatch: 0, + }, + services: { + api: { + useLoadMlUpgradeMode: () => ({ data: { mlUpgradeModeEnabled: false } }), + }, }, - }, - }), -})); + }), + }; +}); jest.mock('./deprecation_types', () => { const TestRow = ({ From 7912af28dd98d2c619cacb184981d48122b207ed Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Thu, 19 Mar 2026 16:59:52 +0100 Subject: [PATCH 9/9] [Upgrade Assistant] Remove unused data streams test context param Made-with: Cursor --- .../deprecation_types/data_streams/actions_table_cell.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx index fb010ee7f83f0..20164ddada23e 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/data_streams/actions_table_cell.test.tsx @@ -37,11 +37,9 @@ const baseCorrectiveAction: DataStreamsAction = { const createContext = ({ status, resolutionType, - excludedActions, }: { status: DataStreamMigrationStatus; resolutionType?: 'reindex' | 'readonly'; - excludedActions?: Array<'readOnly' | 'reindex'>; }): MigrationStateContext => ({ loadDataStreamMetadata: jest.fn, []>(), initMigration: jest.fn(),