mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-15 10:22:34 +00:00
fix(ui): soft deleted assets visible in search suggestions (#22887)
* fix soft deleted assets in search suggestions * add e2e test
This commit is contained in:
parent
6c948e68bc
commit
bf87f1267c
@ -19,13 +19,16 @@ import { getJsonTreeObject } from '../../utils/exploreDiscovery';
|
|||||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||||
|
|
||||||
const table = new TableClass();
|
const table = new TableClass();
|
||||||
|
const table1 = new TableClass();
|
||||||
|
|
||||||
test.describe('Explore Assets Discovery', () => {
|
test.describe('Explore Assets Discovery', () => {
|
||||||
test.beforeAll(async ({ browser }) => {
|
test.beforeAll(async ({ browser }) => {
|
||||||
const { apiContext, afterAction } = await createNewPage(browser);
|
const { apiContext, afterAction } = await createNewPage(browser);
|
||||||
|
|
||||||
await table.create(apiContext);
|
await table.create(apiContext);
|
||||||
|
await table1.create(apiContext);
|
||||||
await table.delete(apiContext, false);
|
await table.delete(apiContext, false);
|
||||||
|
// await table1.delete(apiContext, false);
|
||||||
|
|
||||||
await afterAction();
|
await afterAction();
|
||||||
});
|
});
|
||||||
@ -34,6 +37,7 @@ test.describe('Explore Assets Discovery', () => {
|
|||||||
const { apiContext, afterAction } = await createNewPage(browser);
|
const { apiContext, afterAction } = await createNewPage(browser);
|
||||||
|
|
||||||
await table.delete(apiContext);
|
await table.delete(apiContext);
|
||||||
|
await table1.delete(apiContext);
|
||||||
|
|
||||||
await afterAction();
|
await afterAction();
|
||||||
});
|
});
|
||||||
@ -183,4 +187,46 @@ test.describe('Explore Assets Discovery', () => {
|
|||||||
)
|
)
|
||||||
).not.toBeAttached();
|
).not.toBeAttached();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Should not display soft deleted assets in search suggestions', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await table1.visitEntityPage(page);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
await page.getByTestId('manage-button').click();
|
||||||
|
await page.getByTestId('delete-button').click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.locator('.ant-modal-title')
|
||||||
|
.getByText(
|
||||||
|
`Delete table "${
|
||||||
|
table1.entityResponseData.displayName ??
|
||||||
|
table1.entityResponseData.name
|
||||||
|
}"`
|
||||||
|
)
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await page.getByTestId('confirmation-text-input').click();
|
||||||
|
await page.getByTestId('confirmation-text-input').fill('DELETE');
|
||||||
|
|
||||||
|
await expect(page.getByTestId('confirm-button')).toBeEnabled();
|
||||||
|
|
||||||
|
await page.getByTestId('confirm-button').click();
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await expect(page.getByTestId('deleted-badge')).toBeVisible();
|
||||||
|
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
await page.getByTestId('searchBox').click();
|
||||||
|
await page.getByTestId('searchBox').fill(table1.entityResponseData.name);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
page.locator('.ant-popover-inner-content').textContent()
|
||||||
|
).not.toContain(table1.entityResponseData.name);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* 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 { fireEvent, render, screen } from '@testing-library/react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useTourProvider } from '../../context/TourProvider/TourProvider';
|
||||||
|
import { SearchIndex } from '../../enums/search.enum';
|
||||||
|
import { searchQuery } from '../../rest/searchAPI';
|
||||||
|
import Suggestions from './Suggestions';
|
||||||
|
|
||||||
|
// Mock dependencies
|
||||||
|
jest.mock('../../rest/searchAPI');
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: jest.fn(),
|
||||||
|
}));
|
||||||
|
jest.mock('../../context/TourProvider/TourProvider');
|
||||||
|
jest.mock('../../utils/SearchUtils', () => ({
|
||||||
|
filterOptionsByIndex: jest.fn((options, index) => {
|
||||||
|
return options.filter((option: any) => option._index === index);
|
||||||
|
}),
|
||||||
|
getGroupLabel: jest.fn((index) => `Group ${index}`),
|
||||||
|
getSuggestionElement: jest.fn((suggestion) => (
|
||||||
|
<div data-testid={`suggestion-${suggestion._id}`} key={suggestion._id}>
|
||||||
|
{suggestion._source.name}
|
||||||
|
</div>
|
||||||
|
)),
|
||||||
|
}));
|
||||||
|
jest.mock('../../utils/SearchClassBase', () => ({
|
||||||
|
getEntitiesSuggestions: jest.fn(() => []),
|
||||||
|
}));
|
||||||
|
jest.mock('../../utils/CommonUtils', () => ({
|
||||||
|
Transi18next: ({ i18nKey, values }: { i18nKey: string; values: any }) => (
|
||||||
|
<span data-testid="transi18next">
|
||||||
|
{i18nKey} {values?.keyword || ''}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock location.search for the component
|
||||||
|
Object.defineProperty(window, 'location', {
|
||||||
|
value: {
|
||||||
|
search: '',
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockSearchQuery = searchQuery as jest.MockedFunction<typeof searchQuery>;
|
||||||
|
const mockUseTranslation = useTranslation as jest.MockedFunction<
|
||||||
|
typeof useTranslation
|
||||||
|
>;
|
||||||
|
const mockUseTourProvider = useTourProvider as jest.MockedFunction<
|
||||||
|
typeof useTourProvider
|
||||||
|
>;
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
searchText: 'test',
|
||||||
|
setIsOpen: jest.fn(),
|
||||||
|
isOpen: true,
|
||||||
|
searchCriteria: SearchIndex.TABLE,
|
||||||
|
isNLPActive: false,
|
||||||
|
onSearchTextUpdate: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Suggestions Component', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
mockUseTranslation.mockReturnValue({
|
||||||
|
t: jest.fn((key: string) => key),
|
||||||
|
i18n: { language: 'en' },
|
||||||
|
} as any);
|
||||||
|
mockUseTourProvider.mockReturnValue({
|
||||||
|
isTourOpen: false,
|
||||||
|
updateTourPage: jest.fn(),
|
||||||
|
updateTourSearch: jest.fn(),
|
||||||
|
} as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AI Query Suggestions', () => {
|
||||||
|
it('should render all AI query suggestions', () => {
|
||||||
|
render(<Suggestions {...defaultProps} isNLPActive searchText="" />);
|
||||||
|
|
||||||
|
const aiQueries = [
|
||||||
|
'Tables owned by marketing',
|
||||||
|
'Tables with Tier1 classification',
|
||||||
|
'Find dashboards tagged with PII.Sensitive',
|
||||||
|
'Topics with schema fields containing address',
|
||||||
|
'Tables tagged with tier1 or tier2',
|
||||||
|
];
|
||||||
|
|
||||||
|
aiQueries.forEach((query) => {
|
||||||
|
expect(screen.getByText(query)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onSearchTextUpdate when AI query button is clicked', () => {
|
||||||
|
const mockOnSearchTextUpdate = jest.fn();
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Suggestions
|
||||||
|
{...defaultProps}
|
||||||
|
isNLPActive
|
||||||
|
searchText=""
|
||||||
|
onSearchTextUpdate={mockOnSearchTextUpdate}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstQueryButton = screen.getByText('Tables owned by marketing');
|
||||||
|
fireEvent.click(firstQueryButton);
|
||||||
|
|
||||||
|
expect(mockOnSearchTextUpdate).toHaveBeenCalledWith(
|
||||||
|
'Tables owned by marketing'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Props Handling', () => {
|
||||||
|
it('should handle empty searchText', () => {
|
||||||
|
render(<Suggestions {...defaultProps} searchText="" />);
|
||||||
|
|
||||||
|
expect(mockSearchQuery).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle isNLPActive prop correctly', () => {
|
||||||
|
render(<Suggestions {...defaultProps} isNLPActive />);
|
||||||
|
|
||||||
|
expect(mockSearchQuery).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Component Behavior', () => {
|
||||||
|
it('should show no results message when searchText is provided but no results', () => {
|
||||||
|
render(<Suggestions {...defaultProps} />);
|
||||||
|
|
||||||
|
// The component should show the no results message
|
||||||
|
expect(screen.getByTestId('transi18next')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call searchQuery when tour is open', () => {
|
||||||
|
mockUseTourProvider.mockReturnValue({
|
||||||
|
isTourOpen: true,
|
||||||
|
updateTourPage: jest.fn(),
|
||||||
|
updateTourSearch: jest.fn(),
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
render(<Suggestions {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(mockSearchQuery).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -292,6 +292,7 @@ const Suggestions = ({
|
|||||||
searchIndex: searchCriteria ?? SearchIndex.DATA_ASSET,
|
searchIndex: searchCriteria ?? SearchIndex.DATA_ASSET,
|
||||||
queryFilter: quickFilter,
|
queryFilter: quickFilter,
|
||||||
pageSize: PAGE_SIZE_BASE,
|
pageSize: PAGE_SIZE_BASE,
|
||||||
|
includeDeleted: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
setOptions(res.hits.hits as unknown as Option[]);
|
setOptions(res.hits.hits as unknown as Option[]);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user