/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import * as React from 'react';
import { VectorUploadOptions } from './vector_upload_options';
import { screen, render, waitFor } from '@testing-library/react';
import { fireEvent } from '@testing-library/dom';
import '@testing-library/jest-dom';
import * as serviceApiCalls from '../services';
jest.mock('../../../../src/plugins/opensearch_dashboards_react/public', () => ({
useOpenSearchDashboards: jest.fn().mockReturnValue({
services: {
http: {
post: () => {
Promise.resolve({});
},
},
notifications: {
toasts: { addSuccess: jest.fn(), addDanger: jest.fn(), addWarning: jest.fn() },
},
},
}),
toMountPoint: jest.fn().mockReturnValue({}),
}));
describe('vector_upload_options', () => {
const props = {};
const getIndexResponseWhenIndexIsNotPresent = {
ok: false,
resp: [],
};
const getIndexResponseWhenIndexIsPresent = {
ok: true,
resp: [
{
health: 'yellow',
index: 'sample-map',
status: 'open',
},
],
};
const failedPostGeojsonResponse = {
ok: false,
resp: {},
};
const singlePartialFailurePostGeojsonResponse = {
ok: true,
resp: {
took: 1969,
errors: true,
total: 3220,
success: 3219,
failure: 1,
failures: [],
},
};
const multiplePartialFailuresPostGeojsonResponse = {
ok: true,
resp: {
took: 1969,
errors: true,
total: 3220,
success: 3218,
failure: 2,
failures: [],
},
};
const noDocumentsIndexedPostGeojsonResponse = {
ok: true,
resp: {
took: 1969,
errors: true,
total: 3220,
success: 0,
failure: 3220,
failures: [],
},
};
const multipleSuccessfulPostGeojsonResponse = {
ok: true,
resp: {
took: 1969,
errors: true,
total: 3220,
success: 3220,
failure: 0,
failures: [],
},
};
const singleSuccessfulPostGeojsonResponse = {
ok: true,
resp: {
took: 1969,
errors: true,
total: 1,
success: 1,
failure: 0,
failures: [],
},
};
const vectorUploadOptionsWithIndexNameUtil = (userEnteredIndexName: string, message: string) => {
render();
const indexName = screen.getByTestId('customIndex');
fireEvent.change(indexName, { target: { value: userEnteredIndexName } });
const button = screen.getByRole('button', { name: 'import-file-button' });
fireEvent.click(button);
expect(screen.getByText(message)).toBeInTheDocument();
};
const vectorUploadOptionsWithIndexNameRendererUtil = async (
userEnteredIndexName: string,
message: string
) => {
const tree = render();
const indexName = screen.getByTestId('customIndex');
fireEvent.change(indexName, { target: { value: userEnteredIndexName } });
const button = screen.getByRole('button', { name: 'import-file-button' });
fireEvent.click(button);
console.log(tree.getByText('errorIndexName'));
await expect(tree.findAllByText(message)).toBeTruthy();
};
const addUserInputToDOM = async () => {
const jsonData = {
type: 'FeatureCollection',
name: 'sample',
features: [
{
type: 'Feature',
properties: { name: 'sample' },
geometry: { type: 'Polygon', coordinates: [] },
},
],
};
const { getByTestId } = render();
const indexName = screen.getByTestId('customIndex');
fireEvent.change(indexName, { target: { value: 'sample' } });
const uploader = getByTestId('filePicker');
const str = JSON.stringify([jsonData]);
const blob = new Blob([str]);
const file = new File([blob], 'sample.json', { type: 'application/JSON' });
File.prototype.text = jest.fn().mockResolvedValueOnce(JSON.stringify([jsonData]));
await fireEvent.change(uploader, {
target: { files: [file] },
});
await expect(uploader.files[0].name).toBe('sample.json');
};
it('renders the VectorUploadOptions based on props provided', () => {
const vectorUploadOptions = render();
expect(vectorUploadOptions).toMatchSnapshot();
});
it('renders the VectorUploadOptions component with error message when index name is invalid', () => {
vectorUploadOptionsWithIndexNameUtil(
'+abc',
"Map name can't start with + , _ , - or . It should start with a-z."
);
});
it('renders the VectorUploadOptions component with error message when index name is greater than 250 characters', () => {
vectorUploadOptionsWithIndexNameUtil(
'berhtoe7k9yyl43uuzlh6hqsc00iunkqu49110u3kxizck9hy6f584mfaksjcx3zekntyid2tqy39msp25kp0r1gnib5noqmtz1hatq3s4lsbluwrfljrglt7sg3fp1uebukm1ycvh1onrylwrogclvhpf7npzhcfbrvcybmofee5sflwnsx2xxkgqjfsrsg7nz032jlmm0cpahltdekhyg66pcv2plukby8fgm3vze9jhewrilre07kdakb0ul7',
'Map name should be less than 250 characters.'
);
});
it('renders the VectorUploadOptions component with error message when index name has upper case letters', async () => {
try {
await vectorUploadOptionsWithIndexNameRendererUtil(
'ABC',
'Upper case letters are not allowed'
);
} catch (err) {}
});
it('renders the VectorUploadOptions component with error message when index name has special characters', async () => {
try {
await vectorUploadOptionsWithIndexNameRendererUtil(
'a#bc',
'Special characters are not allowed.'
);
} catch (err) {}
});
it('renders the VectorUploadOptions component with error message when index name has -map as suffix', async () => {
try {
await vectorUploadOptionsWithIndexNameRendererUtil(
'sample-map',
"Map name can't end with -map."
);
} catch (err) {}
});
it('renders the VectorUploadOptions component when we have successfully indexed all the data having multiple features', async () => {
addUserInputToDOM();
console.log('test case for successfully indexed file data');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsNotPresent);
});
jest.spyOn(serviceApiCalls, 'postGeojson').mockImplementation(() => {
return Promise.resolve(multipleSuccessfulPostGeojsonResponse);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders the VectorUploadOptions component when we have successfully indexed the data having single feature', async () => {
addUserInputToDOM();
console.log('test case for successfully indexed file data');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsNotPresent);
});
jest.spyOn(serviceApiCalls, 'postGeojson').mockImplementation(() => {
return Promise.resolve(singleSuccessfulPostGeojsonResponse);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders the VectorUploadOptions component when we have a single failure during indexing', async () => {
addUserInputToDOM();
console.log('test case for partial failures during indexing');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsNotPresent);
});
jest.spyOn(serviceApiCalls, 'postGeojson').mockImplementation(() => {
return Promise.resolve(singlePartialFailurePostGeojsonResponse);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders the VectorUploadOptions component when we have multiple partial failures during indexing', async () => {
addUserInputToDOM();
console.log('test case for partial failures during indexing');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsNotPresent);
});
jest.spyOn(serviceApiCalls, 'postGeojson').mockImplementation(() => {
return Promise.resolve(multiplePartialFailuresPostGeojsonResponse);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders the VectorUploadOptions component when all the documents fail to index', async () => {
addUserInputToDOM();
console.log('test case for failed documents');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsNotPresent);
});
jest.spyOn(serviceApiCalls, 'postGeojson').mockImplementation(() => {
return Promise.resolve(noDocumentsIndexedPostGeojsonResponse);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders the VectorUploadOptions component when postGeojson call fails', async () => {
addUserInputToDOM();
console.log('test case for call failure to postGeojson');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsNotPresent);
});
jest.spyOn(serviceApiCalls, 'postGeojson').mockImplementation(() => {
return Promise.resolve(failedPostGeojsonResponse);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders the VectorUploadOptions component when getIndex returns a duplicate index', async () => {
addUserInputToDOM();
console.log('test case for duplicate index check');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsPresent);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders the VectorUploadOptions component when uploaded file size is zero', async () => {
const { getByTestId } = render();
const indexName = screen.getByTestId('customIndex');
fireEvent.change(indexName, { target: { value: 'sample' } });
const uploader = getByTestId('filePicker');
const file = new File([], 'sample.json', { type: 'application/JSON' });
File.prototype.text = jest.fn().mockResolvedValueOnce(JSON.stringify([]));
await fireEvent.change(uploader, {
target: { files: [file] },
});
await expect(uploader.files[0].name).toBe('sample.json');
const button = screen.getByRole('button', { name: 'import-file-button' });
jest.spyOn(serviceApiCalls, 'getIndex').mockImplementation(() => {
return Promise.resolve(getIndexResponseWhenIndexIsNotPresent);
});
jest.spyOn(serviceApiCalls, 'postGeojson').mockImplementation(() => {
return Promise.resolve(multipleSuccessfulPostGeojsonResponse);
});
await waitFor(() => {
fireEvent.click(button);
});
});
it('renders danger toast as file format is not geojson or json', async () => {
const { getByTestId } = render();
const indexName = screen.getByTestId('customIndex');
fireEvent.change(indexName, { target: { value: 'sample' } });
const uploader = getByTestId('filePicker');
const file = new File([], 'sample.txt', { type: 'text/plain' });
File.prototype.text = jest.fn().mockResolvedValueOnce(JSON.stringify([]));
await fireEvent.change(uploader, {
target: { files: [file] },
});
await expect(uploader.files[0].name).toBe('sample.txt');
});
it('renders the VectorUploadOptions component when uploaded file size is >25 MB', async () => {
const { getByTestId } = render();
const indexName = screen.getByTestId('customIndex');
fireEvent.change(indexName, { target: { value: 'sample' } });
const uploader = getByTestId('filePicker');
const jsonData = {
type: 'FeatureCollection',
name: 'sample',
features: [
{
type: 'Feature',
properties: { name: 'sample' },
geometry: { type: 'Polygon', coordinates: [] },
},
],
};
const jsonDataArray = [];
const max = 100000;
for (; jsonDataArray.push(jsonData) < max; );
const str = JSON.stringify(jsonDataArray);
const blob = new Blob([str]);
const file = new File([blob], 'sample.json', { type: 'application/JSON' });
File.prototype.text = jest.fn().mockResolvedValueOnce(JSON.stringify(jsonDataArray));
await fireEvent.change(uploader, {
target: { files: [file] },
});
await expect(uploader.files[0].name).toBe('sample.json');
});
});