mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-16 19:28:21 +00:00
MINOR: supported all_index in search index configuration form (#16571)
* supported all_index in search index configuration form * allow clear in select widget * supported tree select for the entities * playwright test * added env for the run application status test * fix beta badge color, color of checkbox and changes as per comments * minor fix * fix sonar issue
This commit is contained in:
parent
27f4d9799e
commit
15ae2d3cc3
@ -29,13 +29,14 @@
|
|||||||
"dashboardService",
|
"dashboardService",
|
||||||
"pipelineService",
|
"pipelineService",
|
||||||
"mlmodelService",
|
"mlmodelService",
|
||||||
|
"storageService",
|
||||||
|
"metadataService",
|
||||||
"searchService",
|
"searchService",
|
||||||
"entityReportData",
|
"entityReportData",
|
||||||
"webAnalyticEntityViewReportData",
|
"webAnalyticEntityViewReportData",
|
||||||
"webAnalyticUserActivityReportData",
|
"webAnalyticUserActivityReportData",
|
||||||
"domain",
|
"domain",
|
||||||
"storedProcedure",
|
"storedProcedure",
|
||||||
"storageService",
|
|
||||||
"dataProduct",
|
"dataProduct",
|
||||||
"testCaseResolutionStatus"
|
"testCaseResolutionStatus"
|
||||||
],
|
],
|
||||||
|
@ -43,14 +43,16 @@
|
|||||||
"dashboardService",
|
"dashboardService",
|
||||||
"pipelineService",
|
"pipelineService",
|
||||||
"mlmodelService",
|
"mlmodelService",
|
||||||
|
"storageService",
|
||||||
|
"metadataService",
|
||||||
"searchService",
|
"searchService",
|
||||||
"entityReportData",
|
"entityReportData",
|
||||||
"webAnalyticEntityViewReportData",
|
"webAnalyticEntityViewReportData",
|
||||||
"webAnalyticUserActivityReportData",
|
"webAnalyticUserActivityReportData",
|
||||||
"domain",
|
"domain",
|
||||||
"storedProcedure",
|
"storedProcedure",
|
||||||
"storageService",
|
"dataProduct",
|
||||||
"dataProduct"
|
"testCaseResolutionStatus"
|
||||||
],
|
],
|
||||||
"recreateIndex": true,
|
"recreateIndex": true,
|
||||||
"batchSize": "100",
|
"batchSize": "100",
|
||||||
|
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import test, { expect, Page } from '@playwright/test';
|
||||||
|
import { GlobalSettingOptions } from '../../constant/settings';
|
||||||
|
import { getApiContext, redirectToHomePage } from '../../utils/common';
|
||||||
|
import { settingClick } from '../../utils/sidebar';
|
||||||
|
|
||||||
|
// use the admin user to login
|
||||||
|
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||||
|
|
||||||
|
const verifyApplicationTriggerToastData = async (page: Page) => {
|
||||||
|
await expect(page.getByRole('alert').first()).toHaveText(
|
||||||
|
/Application triggered successfully/
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByLabel('close').first().click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyLastExecutionStatus = async (page: Page) => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
|
||||||
|
await expect
|
||||||
|
.poll(
|
||||||
|
async () => {
|
||||||
|
const response = await apiContext
|
||||||
|
.get(
|
||||||
|
'/api/v1/apps/name/SearchIndexingApplication/status?offset=0&limit=1'
|
||||||
|
)
|
||||||
|
.then((res) => res.json());
|
||||||
|
|
||||||
|
return response.data[0]?.status;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Custom expect message for reporting, optional.
|
||||||
|
message: 'To get the last run execution status as success',
|
||||||
|
intervals: [30_000],
|
||||||
|
timeout: 300_000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toBe('success');
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="app-run-history-table"]');
|
||||||
|
|
||||||
|
await expect(page.getByTestId('pipeline-status')).toContainText('Success');
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyLastExecutionRun = async (page: Page) => {
|
||||||
|
const response = await page.waitForResponse(
|
||||||
|
'/api/v1/apps/name/SearchIndexingApplication/status?offset=0&limit=1'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status()).toBe(200);
|
||||||
|
|
||||||
|
const responseData = await response.json();
|
||||||
|
if (responseData.data.length > 0) {
|
||||||
|
expect(responseData.data).toHaveLength(1);
|
||||||
|
|
||||||
|
if (responseData.data[0].status === 'running') {
|
||||||
|
// wait for success status
|
||||||
|
await verifyLastExecutionStatus(page);
|
||||||
|
} else {
|
||||||
|
expect(responseData.data[0].status).toBe('success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test('Search Index Application', async ({ page }) => {
|
||||||
|
await test.step('Visit Application page', async () => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
await settingClick(page, GlobalSettingOptions.APPLICATIONS);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Verify last execution run', async () => {
|
||||||
|
await page
|
||||||
|
.locator(
|
||||||
|
'[data-testid="search-indexing-application-card"] [data-testid="config-btn"]'
|
||||||
|
)
|
||||||
|
.click();
|
||||||
|
await verifyLastExecutionRun(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Edit application', async () => {
|
||||||
|
await page.click('[data-testid="edit-button"]');
|
||||||
|
await page.click('[data-testid="cron-type"]');
|
||||||
|
await page.click('.rc-virtual-list [title="None"]');
|
||||||
|
|
||||||
|
const deployResponse = page.waitForResponse('/api/v1/apps/*');
|
||||||
|
await page.click('.ant-modal-body [data-testid="deploy-button"]');
|
||||||
|
await deployResponse;
|
||||||
|
|
||||||
|
await expect(page.getByRole('alert').first()).toHaveText(
|
||||||
|
/Schedule saved successfully/
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByLabel('close').first().click();
|
||||||
|
|
||||||
|
expect(await page.innerText('[data-testid="schedule-type"]')).toContain(
|
||||||
|
'None'
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.click('[data-testid="configuration"]');
|
||||||
|
await page.fill('#root\\/batchSize', '0');
|
||||||
|
|
||||||
|
await page.getByTitle('chart').getByLabel('close').click();
|
||||||
|
|
||||||
|
await page.click(
|
||||||
|
'[data-testid="select-widget"] > .ant-select-selector > .ant-select-selection-item'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="select-option-JP"]');
|
||||||
|
|
||||||
|
const responseAfterSubmit = page.waitForResponse('/api/v1/apps/*');
|
||||||
|
await page.click('[data-testid="submit-btn"]');
|
||||||
|
await responseAfterSubmit;
|
||||||
|
|
||||||
|
await expect(page.getByRole('alert').first()).toHaveText(
|
||||||
|
/Configuration saved successfully/
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByLabel('close').first().click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Uninstall application', async () => {
|
||||||
|
await page.click('[data-testid="manage-button"]');
|
||||||
|
await page.click('[data-testid="uninstall-button-title"]');
|
||||||
|
|
||||||
|
const deleteRequest = page.waitForResponse(
|
||||||
|
'/api/v1/apps/name/SearchIndexingApplication?hardDelete=true'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="save-button"]');
|
||||||
|
await deleteRequest;
|
||||||
|
|
||||||
|
await expect(page.getByRole('alert').first()).toHaveText(
|
||||||
|
/Application uninstalled/
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByLabel('close').first().click();
|
||||||
|
|
||||||
|
const card1 = page.locator(
|
||||||
|
'[data-testid="search-indexing-application-card"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(await card1.isVisible()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Install application', async () => {
|
||||||
|
await page.click('[data-testid="add-application"]');
|
||||||
|
|
||||||
|
// Verify response status code
|
||||||
|
const getMarketPlaceResponse = await page.waitForResponse(
|
||||||
|
'/api/v1/apps/marketplace?limit=*'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getMarketPlaceResponse.status()).toBe(200);
|
||||||
|
|
||||||
|
await page.click(
|
||||||
|
'[data-testid="search-indexing-application-card"] [data-testid="config-btn"]'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="install-application"]');
|
||||||
|
await page.click('[data-testid="save-button"]');
|
||||||
|
await page.click('[data-testid="submit-btn"]');
|
||||||
|
await page.click('[data-testid="cron-type"]');
|
||||||
|
await page.click('.rc-virtual-list [title="None"]');
|
||||||
|
|
||||||
|
expect(await page.innerText('[data-testid="cron-type"]')).toContain('None');
|
||||||
|
|
||||||
|
const installApplicationResponse = page.waitForResponse('api/v1/apps');
|
||||||
|
await page.click('[data-testid="deploy-button"]');
|
||||||
|
await installApplicationResponse;
|
||||||
|
|
||||||
|
await expect(page.getByRole('alert').first()).toHaveText(
|
||||||
|
/Application installed successfully/
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByLabel('close').first().click();
|
||||||
|
|
||||||
|
const card = page.locator(
|
||||||
|
'[data-testid="search-indexing-application-card"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(await card.isVisible()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (process.env.isOss) {
|
||||||
|
await test.step('Run application', async () => {
|
||||||
|
test.slow(true); // Test time shouldn't exceed while re-fetching the history API.
|
||||||
|
|
||||||
|
await page.click(
|
||||||
|
'[data-testid="search-indexing-application-card"] [data-testid="config-btn"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
const triggerPipelineResponse = page.waitForResponse(
|
||||||
|
'/api/v1/apps/trigger/SearchIndexingApplication'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="run-now-button"]');
|
||||||
|
|
||||||
|
await triggerPipelineResponse;
|
||||||
|
|
||||||
|
await verifyApplicationTriggerToastData(page);
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await verifyLastExecutionRun(page);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -12,8 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
import { APIRequestContext, expect, Page } from '@playwright/test';
|
import { APIRequestContext, expect, Page } from '@playwright/test';
|
||||||
import {
|
import {
|
||||||
ENTITY_PATH,
|
|
||||||
EntityTypeEndpoint,
|
EntityTypeEndpoint,
|
||||||
|
ENTITY_PATH,
|
||||||
} from '../support/entity/Entity.interface';
|
} from '../support/entity/Entity.interface';
|
||||||
import { UserClass } from '../support/user/UserClass';
|
import { UserClass } from '../support/user/UserClass';
|
||||||
import { uuid } from './common';
|
import { uuid } from './common';
|
||||||
|
@ -28,7 +28,6 @@ import { DatabaseServiceType } from '../../../../../generated/entity/data/databa
|
|||||||
import { MetadataServiceType } from '../../../../../generated/entity/services/metadataService';
|
import { MetadataServiceType } from '../../../../../generated/entity/services/metadataService';
|
||||||
import { MlModelServiceType } from '../../../../../generated/entity/services/mlmodelService';
|
import { MlModelServiceType } from '../../../../../generated/entity/services/mlmodelService';
|
||||||
import { PipelineServiceType } from '../../../../../generated/entity/services/pipelineService';
|
import { PipelineServiceType } from '../../../../../generated/entity/services/pipelineService';
|
||||||
import { useApplicationStore } from '../../../../../hooks/useApplicationStore';
|
|
||||||
import { errorMsg, getServiceLogo } from '../../../../../utils/CommonUtils';
|
import { errorMsg, getServiceLogo } from '../../../../../utils/CommonUtils';
|
||||||
import ServiceUtilClassBase from '../../../../../utils/ServiceUtilClassBase';
|
import ServiceUtilClassBase from '../../../../../utils/ServiceUtilClassBase';
|
||||||
import Searchbar from '../../../../common/SearchBarComponent/SearchBar.component';
|
import Searchbar from '../../../../common/SearchBarComponent/SearchBar.component';
|
||||||
@ -44,7 +43,6 @@ const SelectServiceType = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
onNext,
|
onNext,
|
||||||
}: SelectServiceTypeProps) => {
|
}: SelectServiceTypeProps) => {
|
||||||
const { theme } = useApplicationStore();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [category, setCategory] = useState('');
|
const [category, setCategory] = useState('');
|
||||||
const [connectorSearchTerm, setConnectorSearchTerm] = useState('');
|
const [connectorSearchTerm, setConnectorSearchTerm] = useState('');
|
||||||
@ -146,11 +144,7 @@ const SelectServiceType = ({
|
|||||||
{BETA_SERVICES.includes(
|
{BETA_SERVICES.includes(
|
||||||
type as DatabaseServiceType | PipelineServiceType
|
type as DatabaseServiceType | PipelineServiceType
|
||||||
) ? (
|
) ? (
|
||||||
<Badge
|
<Badge className="service-beta-tag" count={t('label.beta')} />
|
||||||
className="service-beta-tag"
|
|
||||||
color={theme.primaryColor}
|
|
||||||
count={t('label.beta')}
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
</p>
|
</p>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -22,32 +22,50 @@ import {
|
|||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MOCK_SELECT_WIDGET } from '../../../../../mocks/SelectWidget.mock';
|
import {
|
||||||
|
MOCK_SELECT_WIDGET,
|
||||||
|
MOCK_TREE_SELECT_WIDGET,
|
||||||
|
} from '../../../../../mocks/SelectWidget.mock';
|
||||||
import SelectWidget from './SelectWidget';
|
import SelectWidget from './SelectWidget';
|
||||||
|
|
||||||
|
jest.mock('./TreeSelectWidget', () =>
|
||||||
|
jest.fn().mockImplementation(() => <p>TreeSelectWidget</p>)
|
||||||
|
);
|
||||||
|
|
||||||
const mockOnFocus = jest.fn();
|
const mockOnFocus = jest.fn();
|
||||||
const mockOnBlur = jest.fn();
|
const mockOnBlur = jest.fn();
|
||||||
const mockOnChange = jest.fn();
|
const mockOnChange = jest.fn();
|
||||||
|
|
||||||
const mockProps: WidgetProps = {
|
const mockBaseProps = {
|
||||||
onFocus: mockOnFocus,
|
onFocus: mockOnFocus,
|
||||||
onBlur: mockOnBlur,
|
onBlur: mockOnBlur,
|
||||||
onChange: mockOnChange,
|
onChange: mockOnChange,
|
||||||
registry: {} as Registry,
|
registry: {} as Registry,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockSelectProps: WidgetProps = {
|
||||||
|
...mockBaseProps,
|
||||||
...MOCK_SELECT_WIDGET,
|
...MOCK_SELECT_WIDGET,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockTreeSelectProps: WidgetProps = {
|
||||||
|
...mockBaseProps,
|
||||||
|
...MOCK_TREE_SELECT_WIDGET,
|
||||||
|
};
|
||||||
|
|
||||||
describe('Test SelectWidget Component', () => {
|
describe('Test SelectWidget Component', () => {
|
||||||
it('Should render select component', async () => {
|
it('Should render select component', async () => {
|
||||||
render(<SelectWidget {...mockProps} />);
|
render(<SelectWidget {...mockSelectProps} />);
|
||||||
|
|
||||||
const selectInput = screen.getByTestId('select-widget');
|
const selectInput = screen.getByTestId('select-widget');
|
||||||
|
const treeSelectWidget = screen.queryByText('TreeSelectWidget');
|
||||||
|
|
||||||
expect(selectInput).toBeInTheDocument();
|
expect(selectInput).toBeInTheDocument();
|
||||||
|
expect(treeSelectWidget).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be disabled', async () => {
|
it('Should be disabled', async () => {
|
||||||
render(<SelectWidget {...mockProps} disabled />);
|
render(<SelectWidget {...mockSelectProps} disabled />);
|
||||||
|
|
||||||
const selectInput = await findByRole(
|
const selectInput = await findByRole(
|
||||||
screen.getByTestId('select-widget'),
|
screen.getByTestId('select-widget'),
|
||||||
@ -58,7 +76,7 @@ describe('Test SelectWidget Component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should call onFocus', async () => {
|
it('Should call onFocus', async () => {
|
||||||
render(<SelectWidget {...mockProps} />);
|
render(<SelectWidget {...mockSelectProps} />);
|
||||||
|
|
||||||
const selectInput = screen.getByTestId('select-widget');
|
const selectInput = screen.getByTestId('select-widget');
|
||||||
|
|
||||||
@ -68,7 +86,7 @@ describe('Test SelectWidget Component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should call onBlur', async () => {
|
it('Should call onBlur', async () => {
|
||||||
render(<SelectWidget {...mockProps} />);
|
render(<SelectWidget {...mockSelectProps} />);
|
||||||
|
|
||||||
const selectInput = screen.getByTestId('select-widget');
|
const selectInput = screen.getByTestId('select-widget');
|
||||||
|
|
||||||
@ -78,7 +96,7 @@ describe('Test SelectWidget Component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should call onChange', async () => {
|
it('Should call onChange', async () => {
|
||||||
render(<SelectWidget {...mockProps} />);
|
render(<SelectWidget {...mockSelectProps} />);
|
||||||
|
|
||||||
const selectInput = await findByRole(
|
const selectInput = await findByRole(
|
||||||
screen.getByTestId('select-widget'),
|
screen.getByTestId('select-widget'),
|
||||||
@ -97,4 +115,14 @@ describe('Test SelectWidget Component', () => {
|
|||||||
|
|
||||||
expect(mockOnChange).toHaveBeenCalledTimes(1);
|
expect(mockOnChange).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should render TreeSelectWidget component if uiFieldType is treeSelect', async () => {
|
||||||
|
render(<SelectWidget {...mockTreeSelectProps} />);
|
||||||
|
|
||||||
|
const selectWidget = screen.queryByTestId('select-widget');
|
||||||
|
const treeSelectWidget = screen.getByText('TreeSelectWidget');
|
||||||
|
|
||||||
|
expect(treeSelectWidget).toBeInTheDocument();
|
||||||
|
expect(selectWidget).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,15 +14,18 @@ import { WidgetProps } from '@rjsf/utils';
|
|||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import { capitalize } from 'lodash';
|
import { capitalize } from 'lodash';
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
import TreeSelectWidget from './TreeSelectWidget';
|
||||||
|
|
||||||
|
const SelectWidget: FC<WidgetProps> = (props) => {
|
||||||
|
if (props.schema.uiFieldType === 'treeSelect') {
|
||||||
|
return <TreeSelectWidget {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { onFocus, onBlur, onChange, ...rest } = props;
|
||||||
|
|
||||||
const SelectWidget: FC<WidgetProps> = ({
|
|
||||||
onFocus,
|
|
||||||
onBlur,
|
|
||||||
onChange,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
allowClear
|
||||||
autoFocus={rest.autofocus}
|
autoFocus={rest.autofocus}
|
||||||
className="d-block w-full"
|
className="d-block w-full"
|
||||||
data-testid="select-widget"
|
data-testid="select-widget"
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Registry } from '@rjsf/utils';
|
||||||
|
import {
|
||||||
|
act,
|
||||||
|
findByRole,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitForElement,
|
||||||
|
} from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import React from 'react';
|
||||||
|
import { MOCK_TREE_SELECT_WIDGET } from '../../../../../mocks/SelectWidget.mock';
|
||||||
|
import TreeSelectWidget from './TreeSelectWidget';
|
||||||
|
|
||||||
|
const mockOnFocus = jest.fn();
|
||||||
|
const mockOnBlur = jest.fn();
|
||||||
|
const mockOnChange = jest.fn();
|
||||||
|
|
||||||
|
const mockProps = {
|
||||||
|
onFocus: mockOnFocus,
|
||||||
|
onBlur: mockOnBlur,
|
||||||
|
onChange: mockOnChange,
|
||||||
|
registry: {} as Registry,
|
||||||
|
...MOCK_TREE_SELECT_WIDGET,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Test TreeSelectWidget Component', () => {
|
||||||
|
it('Should render tree select component', async () => {
|
||||||
|
render(<TreeSelectWidget {...mockProps} />);
|
||||||
|
|
||||||
|
const treeSelectWidget = screen.getByTestId('tree-select-widget');
|
||||||
|
|
||||||
|
expect(treeSelectWidget).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be disabled', async () => {
|
||||||
|
render(<TreeSelectWidget {...mockProps} disabled />);
|
||||||
|
|
||||||
|
const treeSelectInput = await findByRole(
|
||||||
|
screen.getByTestId('tree-select-widget'),
|
||||||
|
'combobox'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(treeSelectInput).toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should call onFocus', async () => {
|
||||||
|
render(<TreeSelectWidget {...mockProps} />);
|
||||||
|
|
||||||
|
const treeSelectInput = screen.getByTestId('tree-select-widget');
|
||||||
|
|
||||||
|
fireEvent.focus(treeSelectInput);
|
||||||
|
|
||||||
|
expect(mockOnFocus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should call onBlur', async () => {
|
||||||
|
render(<TreeSelectWidget {...mockProps} />);
|
||||||
|
|
||||||
|
const treeSelectInput = screen.getByTestId('tree-select-widget');
|
||||||
|
|
||||||
|
fireEvent.blur(treeSelectInput);
|
||||||
|
|
||||||
|
expect(mockOnBlur).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should call onChange', async () => {
|
||||||
|
render(<TreeSelectWidget {...mockProps} />);
|
||||||
|
|
||||||
|
const treeSelectInput = await findByRole(
|
||||||
|
screen.getByTestId('tree-select-widget'),
|
||||||
|
'combobox'
|
||||||
|
);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
userEvent.click(treeSelectInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForElement(() => screen.getAllByText('Table'));
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(screen.getAllByText('Table')[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockOnChange).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import Icon from '@ant-design/icons/lib/components/Icon';
|
||||||
|
import { WidgetProps } from '@rjsf/utils';
|
||||||
|
import { TreeSelect } from 'antd';
|
||||||
|
import { startCase } from 'lodash';
|
||||||
|
import React, { FC, useMemo } from 'react';
|
||||||
|
import { ReactComponent as ArrowIcon } from '../../../../../assets/svg/ic-arrow-down.svg';
|
||||||
|
import { TEXT_BODY_COLOR } from '../../../../../constants/constants';
|
||||||
|
|
||||||
|
const TreeSelectWidget: FC<WidgetProps> = ({
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
|
onChange,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const treeData = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
title: 'All',
|
||||||
|
value: 'all',
|
||||||
|
key: 'all',
|
||||||
|
children: rest.options.enumOptions?.map((node) => ({
|
||||||
|
title: startCase(node.label),
|
||||||
|
value: node.value,
|
||||||
|
key: node.value,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[rest.options.enumOptions]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TreeSelect
|
||||||
|
allowClear
|
||||||
|
multiple
|
||||||
|
showSearch
|
||||||
|
treeCheckable
|
||||||
|
treeDefaultExpandAll
|
||||||
|
data-testid="tree-select-widget"
|
||||||
|
disabled={rest.disabled}
|
||||||
|
filterTreeNode={false}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
switcherIcon={
|
||||||
|
<Icon
|
||||||
|
component={ArrowIcon}
|
||||||
|
data-testid="expand-icon"
|
||||||
|
style={{ fontSize: '10px', color: TEXT_BODY_COLOR }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
treeData={treeData}
|
||||||
|
onBlur={() => onBlur(rest.id, rest.value)}
|
||||||
|
onChange={onChange}
|
||||||
|
onFocus={() => onFocus(rest.id, rest.value)}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TreeSelectWidget;
|
@ -39,3 +39,172 @@ export const MOCK_SELECT_WIDGET = {
|
|||||||
uiSchema: {},
|
uiSchema: {},
|
||||||
value: 'JP',
|
value: 'JP',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MOCK_TREE_SELECT_WIDGET = {
|
||||||
|
autofocus: false,
|
||||||
|
disabled: false,
|
||||||
|
formContext: { handleFocus: undefined },
|
||||||
|
hideError: undefined,
|
||||||
|
hideLabel: false,
|
||||||
|
id: 'root/entities',
|
||||||
|
label: 'Entities',
|
||||||
|
multiple: true,
|
||||||
|
name: 'entities',
|
||||||
|
readonly: false,
|
||||||
|
placeholder: '',
|
||||||
|
rawErrors: undefined,
|
||||||
|
options: {
|
||||||
|
enumOptions: [
|
||||||
|
{
|
||||||
|
label: 'table',
|
||||||
|
value: 'table',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'dashboard',
|
||||||
|
value: 'dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'topic',
|
||||||
|
value: 'topic',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'pipeline',
|
||||||
|
value: 'pipeline',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ingestionPipeline',
|
||||||
|
value: 'ingestionPipeline',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'searchIndex',
|
||||||
|
value: 'searchIndex',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'user',
|
||||||
|
value: 'user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'team',
|
||||||
|
value: 'team',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'glossary',
|
||||||
|
value: 'glossary',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'glossaryTerm',
|
||||||
|
value: 'glossaryTerm',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'mlmodel',
|
||||||
|
value: 'mlmodel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'tag',
|
||||||
|
value: 'tag',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'classification',
|
||||||
|
value: 'classification',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'query',
|
||||||
|
value: 'query',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'container',
|
||||||
|
value: 'container',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'database',
|
||||||
|
value: 'database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'databaseSchema',
|
||||||
|
value: 'databaseSchema',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'testCase',
|
||||||
|
value: 'testCase',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'testSuite',
|
||||||
|
value: 'testSuite',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'chart',
|
||||||
|
value: 'chart',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'dashboardDataModel',
|
||||||
|
value: 'dashboardDataModel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'databaseService',
|
||||||
|
value: 'databaseService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'messagingService',
|
||||||
|
value: 'messagingService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'dashboardService',
|
||||||
|
value: 'dashboardService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'pipelineService',
|
||||||
|
value: 'pipelineService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'mlmodelService',
|
||||||
|
value: 'mlmodelService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'storageService',
|
||||||
|
value: 'storageService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'metadataService',
|
||||||
|
value: 'metadataService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'searchService',
|
||||||
|
value: 'searchService',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'entityReportData',
|
||||||
|
value: 'entityReportData',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'webAnalyticEntityViewReportData',
|
||||||
|
value: 'webAnalyticEntityViewReportData',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'webAnalyticUserActivityReportData',
|
||||||
|
value: 'webAnalyticUserActivityReportData',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'domain',
|
||||||
|
value: 'domain',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'storedProcedure',
|
||||||
|
value: 'storedProcedure',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'dataProduct',
|
||||||
|
value: 'dataProduct',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'testCaseResolutionStatus',
|
||||||
|
value: 'testCaseResolutionStatus',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
title: 'Entities',
|
||||||
|
description: 'List of entities that you need to reindex',
|
||||||
|
uiFieldType: 'treeSelect',
|
||||||
|
},
|
||||||
|
value: ['all'],
|
||||||
|
};
|
||||||
|
@ -138,4 +138,6 @@
|
|||||||
|
|
||||||
.rjsf .checkbox input[type='checkbox'] {
|
.rjsf .checkbox input[type='checkbox'] {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
accent-color: @primary-color;
|
||||||
}
|
}
|
||||||
|
@ -55,44 +55,8 @@
|
|||||||
"testCaseResolutionStatus"
|
"testCaseResolutionStatus"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default": [
|
"default": ["all"],
|
||||||
"table",
|
"uiFieldType": "treeSelect",
|
||||||
"dashboard",
|
|
||||||
"topic",
|
|
||||||
"pipeline",
|
|
||||||
"searchIndex",
|
|
||||||
"user",
|
|
||||||
"team",
|
|
||||||
"glossary",
|
|
||||||
"glossaryTerm",
|
|
||||||
"mlmodel",
|
|
||||||
"tag",
|
|
||||||
"classification",
|
|
||||||
"query",
|
|
||||||
"container",
|
|
||||||
"database",
|
|
||||||
"databaseSchema",
|
|
||||||
"testCase",
|
|
||||||
"testSuite",
|
|
||||||
"chart",
|
|
||||||
"dashboardDataModel",
|
|
||||||
"databaseService",
|
|
||||||
"messagingService",
|
|
||||||
"dashboardService",
|
|
||||||
"pipelineService",
|
|
||||||
"mlmodelService",
|
|
||||||
"storageService",
|
|
||||||
"metadataService",
|
|
||||||
"searchService",
|
|
||||||
"entityReportData",
|
|
||||||
"webAnalyticEntityViewReportData",
|
|
||||||
"webAnalyticUserActivityReportData",
|
|
||||||
"domain",
|
|
||||||
"storedProcedure",
|
|
||||||
"dataProduct",
|
|
||||||
"ingestionPipeline",
|
|
||||||
"testCaseResolutionStatus"
|
|
||||||
],
|
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
"recreateIndex": {
|
"recreateIndex": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user