Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18473,8 +18473,6 @@
"xpack.idxMgmt.createIndex.modal.indexMode.label": "Indexmodus",
"xpack.idxMgmt.createIndex.modal.indexName.label": "Indexname",
"xpack.idxMgmt.createIndex.modal.invalidName.error": "Der Indexname ist nicht gültig",
"xpack.idxMgmt.createIndex.modal.saveButton": "Erstellen",
"xpack.idxMgmt.createIndex.modal.title": "Index erstellen",
"xpack.idxMgmt.createIndex.successfullyCreatedIndexMessage": "Index erfolgreich erstellt: {indexName}",
"xpack.idxMgmt.createRoute.duplicateTemplateIdErrorMessage": "Es gibt bereits eine Vorlage mit dem Namen \"{name}“.",
"xpack.idxMgmt.createTemplate.cloneTemplatePageTitle": "Klonen Sie die Vorlage „{name}“",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18735,8 +18735,6 @@
"xpack.idxMgmt.createIndex.modal.indexMode.label": "Mode d'index",
"xpack.idxMgmt.createIndex.modal.indexName.label": "Nom de l'index",
"xpack.idxMgmt.createIndex.modal.invalidName.error": "Le nom de l'index n'est pas valide",
"xpack.idxMgmt.createIndex.modal.saveButton": "Créer",
"xpack.idxMgmt.createIndex.modal.title": "Créer un index",
"xpack.idxMgmt.createIndex.successfullyCreatedIndexMessage": "Création réussie de l'index : {indexName}",
"xpack.idxMgmt.createRoute.duplicateTemplateIdErrorMessage": "Un modèle s'appelle déjà \"{name}\".",
"xpack.idxMgmt.createTemplate.cloneTemplatePageTitle": "Cloner le modèle \"{name}\"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18759,8 +18759,6 @@
"xpack.idxMgmt.createIndex.modal.indexMode.label": "インデックスモード",
"xpack.idxMgmt.createIndex.modal.indexName.label": "インデックス名",
"xpack.idxMgmt.createIndex.modal.invalidName.error": "インデックス名が有効ではありません",
"xpack.idxMgmt.createIndex.modal.saveButton": "作成",
"xpack.idxMgmt.createIndex.modal.title": "インデックスの作成",
"xpack.idxMgmt.createIndex.successfullyCreatedIndexMessage": "インデックスの作成が正常に完了しました:{indexName}",
"xpack.idxMgmt.createRoute.duplicateTemplateIdErrorMessage": "''{name}''という名前のテンプレートがすでに存在します。",
"xpack.idxMgmt.createTemplate.cloneTemplatePageTitle": "テンプレート''{name}''の複製",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18749,8 +18749,6 @@
"xpack.idxMgmt.createIndex.modal.indexMode.label": "索引模式",
"xpack.idxMgmt.createIndex.modal.indexName.label": "索引名称",
"xpack.idxMgmt.createIndex.modal.invalidName.error": "索引名称无效",
"xpack.idxMgmt.createIndex.modal.saveButton": "创建",
"xpack.idxMgmt.createIndex.modal.title": "创建索引",
"xpack.idxMgmt.createIndex.successfullyCreatedIndexMessage": "已成功创建索引:{indexName}",
"xpack.idxMgmt.createRoute.duplicateTemplateIdErrorMessage": "已有名称为“{name}”的模板。",
"xpack.idxMgmt.createTemplate.cloneTemplatePageTitle": "克隆模板“{name}”",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,8 @@ export const createCreateIndexActions = () => {
};

const clickCreateIndexSaveButton = async () => {
// `CreateIndexModal` renders the submit button in the modal footer and wires it via `form=...`
// (instead of nesting it inside the <form>). In JSDOM, clicking a submit button triggers
// `requestSubmit`, which throws "Not implemented". We only need the React `onClick` handler,
// so switch the DOM button type to avoid the native submit behavior.
const saveButton = screen.getByTestId('createIndexSaveButton') as HTMLButtonElement;
saveButton.type = 'button';
fireEvent.click(saveButton);
const form = screen.getByTestId('createIndexModalForm');
fireEvent.submit(form);
};

const setIndexName = (name: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,4 @@ export const PLUGIN = {
export const MAX_DOCUMENTS_FOR_CONVERT_TO_LOOKUP_INDEX = 2000000000; // 2 billion documents
export const MAX_SHARDS_FOR_CONVERT_TO_LOOKUP_INDEX = 1; // Single shard

export const PLATFORM_INDEX_MGMT_V2 = 'platform:indexManagementV2';

export const DEFAULT_DOCUMENT_PAGE_SIZE = 10;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import { EuiButton } from '@elastic/eui';

import useObservable from 'react-use/lib/useObservable';
import { CreateIndexModal } from './create_index_modal';
import { CreateIndexModalV2 } from './create_index_modal_v2';
import { useAppContext } from '../../../../app_context';
import { useIsPlatformIndexManagementV2Enabled } from '../../../../hooks/use_is_platform_index_management_v2_enabled';

export interface CreateIndexButtonProps {
loadIndices: () => void;
Expand All @@ -26,12 +24,9 @@ export const CreateIndexButton = ({ loadIndices, share, dataTestSubj }: CreateIn
const {
core: { chrome },
} = useAppContext();
const isPlatformIndexManagementV2Enabled = useIsPlatformIndexManagementV2Enabled();
const [createIndexModalOpen, setCreateIndexModalOpen] = useState<boolean>(false);
const createIndexUrl = share?.url.locators.get('SEARCH_CREATE_INDEX')?.useUrl({});

const IndexModal = isPlatformIndexManagementV2Enabled ? CreateIndexModalV2 : CreateIndexModal;

const activeSolutionId = useObservable(chrome.getActiveSolutionNavId$());

const actionProp =
Expand All @@ -55,7 +50,10 @@ export const CreateIndexButton = ({ loadIndices, share, dataTestSubj }: CreateIn
/>
</EuiButton>
{createIndexModalOpen && (
<IndexModal closeModal={() => setCreateIndexModalOpen(false)} loadIndices={loadIndices} />
<CreateIndexModal
closeModal={() => setCreateIndexModalOpen(false)}
loadIndices={loadIndices}
/>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* 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 { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { EuiThemeProvider } from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n-react';
import { CreateIndexModal } from './create_index_modal';

const mockCreateIndex = jest.fn();
jest.mock('../../../../services', () => ({
createIndex: (...args: unknown[]) => mockCreateIndex(...args),
}));

const mockShowSuccessToast = jest.fn();
jest.mock('../../../../services/notification', () => ({
notificationService: {
showSuccessToast: (...args: unknown[]) => mockShowSuccessToast(...args),
},
}));

jest.mock('./utils', () => ({
generateRandomIndexName: () => 'search-abcd',
isValidIndexName: (name: string) => {
if (!name || name !== name.toLowerCase() || name.length === 0) return false;
return true;
},
}));

const renderModal = (props: Partial<React.ComponentProps<typeof CreateIndexModal>> = {}) => {
const defaultProps = {
closeModal: jest.fn(),
loadIndices: jest.fn(),
};

return render(
<I18nProvider>
<EuiThemeProvider>
<CreateIndexModal {...defaultProps} {...props} />
</EuiThemeProvider>
</I18nProvider>
);
};

describe('CreateIndexModal', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('renders the modal with title and description', () => {
renderModal();
expect(screen.getByText('Create your index')).toBeInTheDocument();
expect(
screen.getByText('An index stores and defines the schema of your data.')
).toBeInTheDocument();
});

it('pre-fills the index name with a generated random name', () => {
renderModal();
const input = screen.getByTestId('createIndexNameFieldText');
expect(input).toHaveValue('search-abcd');
});

it('shows index name and index mode form fields', () => {
renderModal();
expect(screen.getByTestId('createIndexNameFieldText')).toBeInTheDocument();
expect(screen.getByTestId('indexModeField')).toBeInTheDocument();
});

it('shows a validation error for invalid index names', async () => {
renderModal();
const input = screen.getByTestId('createIndexNameFieldText');
await userEvent.clear(input);
await userEvent.type(input, 'INVALID');

expect(screen.getByText('Index name is not valid')).toBeInTheDocument();
});

it('clears validation error when a valid name is entered', async () => {
renderModal();
const input = screen.getByTestId('createIndexNameFieldText');

await userEvent.clear(input);
await userEvent.type(input, 'INVALID');
expect(screen.getByText('Index name is not valid')).toBeInTheDocument();

await userEvent.clear(input);
await userEvent.type(input, 'valid-name');
expect(screen.queryByText('Index name is not valid')).not.toBeInTheDocument();
});

it('calls closeModal when cancel button is clicked', () => {
const closeModal = jest.fn();
renderModal({ closeModal });
fireEvent.click(screen.getByTestId('createIndexCancelButton'));
expect(closeModal).toHaveBeenCalled();
});

it('toggles the API code block when Show/Hide API button is clicked', () => {
renderModal();
expect(screen.getByText('Show API')).toBeInTheDocument();

fireEvent.click(screen.getByTestId('createIndexShowApiButton'));
expect(screen.getByText('Hide API')).toBeInTheDocument();
expect(screen.getByText(/PUT search-abcd/)).toBeInTheDocument();

fireEvent.click(screen.getByTestId('createIndexShowApiButton'));
expect(screen.getByText('Show API')).toBeInTheDocument();
});

it('reflects the selected index mode in the API code block', async () => {
renderModal();
fireEvent.click(screen.getByTestId('createIndexShowApiButton'));

expect(screen.getByText(/\"mode\":\"standard\"/)).toBeInTheDocument();
});

it('creates the index on submit with valid name', async () => {
const closeModal = jest.fn();
const loadIndices = jest.fn();
mockCreateIndex.mockResolvedValue({ error: undefined });

renderModal({ closeModal, loadIndices });
fireEvent.click(screen.getByTestId('createIndexSaveButton'));
Comment thread
seialkali marked this conversation as resolved.

await waitFor(() => {
expect(mockCreateIndex).toHaveBeenCalledWith('search-abcd', 'standard');
});

await waitFor(() => {
expect(mockShowSuccessToast).toHaveBeenCalled();
expect(closeModal).toHaveBeenCalled();
expect(loadIndices).toHaveBeenCalled();
});
});

it('creates the index when pressing Enter in the index name input', async () => {
const closeModal = jest.fn();
const loadIndices = jest.fn();
mockCreateIndex.mockResolvedValue({ error: undefined });

renderModal({ closeModal, loadIndices });
fireEvent.submit(screen.getByTestId('createIndexModalForm'));

await waitFor(() => {
expect(mockCreateIndex).toHaveBeenCalledWith('search-abcd', 'standard');
});

await waitFor(() => {
expect(mockShowSuccessToast).toHaveBeenCalled();
expect(closeModal).toHaveBeenCalled();
expect(loadIndices).toHaveBeenCalled();
});
});

it('displays an error callout when index creation fails', async () => {
mockCreateIndex.mockResolvedValue({ error: { message: 'Index already exists' } });

renderModal();
fireEvent.click(screen.getByTestId('createIndexSaveButton'));

await waitFor(() => {
expect(screen.getByText('Error creating index')).toBeInTheDocument();
expect(screen.getByText(/Index already exists/)).toBeInTheDocument();
});
});

it('does not submit when the index name is invalid', async () => {
renderModal();
const input = screen.getByTestId('createIndexNameFieldText');

await userEvent.clear(input);

fireEvent.click(screen.getByTestId('createIndexSaveButton'));

expect(mockCreateIndex).not.toHaveBeenCalled();
});
});
Loading
Loading