diff --git a/openmetadata-service/src/main/resources/json/data/app/SearchIndexingApplication.json b/openmetadata-service/src/main/resources/json/data/app/SearchIndexingApplication.json
index 5ca7d2ec8217..a9939936e444 100644
--- a/openmetadata-service/src/main/resources/json/data/app/SearchIndexingApplication.json
+++ b/openmetadata-service/src/main/resources/json/data/app/SearchIndexingApplication.json
@@ -29,13 +29,14 @@
"dashboardService",
"pipelineService",
"mlmodelService",
+ "storageService",
+ "metadataService",
"searchService",
"entityReportData",
"webAnalyticEntityViewReportData",
"webAnalyticUserActivityReportData",
"domain",
"storedProcedure",
- "storageService",
"dataProduct",
"testCaseResolutionStatus"
],
@@ -47,4 +48,4 @@
"scheduleTimeline": "Custom",
"cronExpression": "0 0 * * *"
}
-}
\ No newline at end of file
+}
diff --git a/openmetadata-service/src/main/resources/json/data/appMarketPlaceDefinition/SearchIndexingApplication.json b/openmetadata-service/src/main/resources/json/data/appMarketPlaceDefinition/SearchIndexingApplication.json
index a5371efbf407..90d6dbfc4c14 100644
--- a/openmetadata-service/src/main/resources/json/data/appMarketPlaceDefinition/SearchIndexingApplication.json
+++ b/openmetadata-service/src/main/resources/json/data/appMarketPlaceDefinition/SearchIndexingApplication.json
@@ -43,17 +43,19 @@
"dashboardService",
"pipelineService",
"mlmodelService",
+ "storageService",
+ "metadataService",
"searchService",
"entityReportData",
"webAnalyticEntityViewReportData",
"webAnalyticUserActivityReportData",
"domain",
"storedProcedure",
- "storageService",
- "dataProduct"
+ "dataProduct",
+ "testCaseResolutionStatus"
],
"recreateIndex": true,
"batchSize": "100",
"searchIndexMappingLanguage": "EN"
}
-}
\ No newline at end of file
+}
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/SearchIndexApplication.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/SearchIndexApplication.spec.ts
new file mode 100644
index 000000000000..2674df203023
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/SearchIndexApplication.spec.ts
@@ -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);
+ });
+ }
+});
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts
index c6cf6b1d9a0c..834dc5ed78c6 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts
@@ -12,8 +12,8 @@
*/
import { APIRequestContext, expect, Page } from '@playwright/test';
import {
- ENTITY_PATH,
EntityTypeEndpoint,
+ ENTITY_PATH,
} from '../support/entity/Entity.interface';
import { UserClass } from '../support/user/UserClass';
import { uuid } from './common';
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddService/Steps/SelectServiceType.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddService/Steps/SelectServiceType.tsx
index e3722d7eeaed..2568a2510bb5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddService/Steps/SelectServiceType.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddService/Steps/SelectServiceType.tsx
@@ -28,7 +28,6 @@ import { DatabaseServiceType } from '../../../../../generated/entity/data/databa
import { MetadataServiceType } from '../../../../../generated/entity/services/metadataService';
import { MlModelServiceType } from '../../../../../generated/entity/services/mlmodelService';
import { PipelineServiceType } from '../../../../../generated/entity/services/pipelineService';
-import { useApplicationStore } from '../../../../../hooks/useApplicationStore';
import { errorMsg, getServiceLogo } from '../../../../../utils/CommonUtils';
import ServiceUtilClassBase from '../../../../../utils/ServiceUtilClassBase';
import Searchbar from '../../../../common/SearchBarComponent/SearchBar.component';
@@ -44,7 +43,6 @@ const SelectServiceType = ({
onCancel,
onNext,
}: SelectServiceTypeProps) => {
- const { theme } = useApplicationStore();
const { t } = useTranslation();
const [category, setCategory] = useState('');
const [connectorSearchTerm, setConnectorSearchTerm] = useState('');
@@ -146,11 +144,7 @@ const SelectServiceType = ({
{BETA_SERVICES.includes(
type as DatabaseServiceType | PipelineServiceType
) ? (
-
TreeSelectWidget
) +); + const mockOnFocus = jest.fn(); const mockOnBlur = jest.fn(); const mockOnChange = jest.fn(); -const mockProps: WidgetProps = { +const mockBaseProps = { onFocus: mockOnFocus, onBlur: mockOnBlur, onChange: mockOnChange, registry: {} as Registry, +}; + +const mockSelectProps: WidgetProps = { + ...mockBaseProps, ...MOCK_SELECT_WIDGET, }; +const mockTreeSelectProps: WidgetProps = { + ...mockBaseProps, + ...MOCK_TREE_SELECT_WIDGET, +}; + describe('Test SelectWidget Component', () => { it('Should render select component', async () => { - render(