mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-12 15:57:44 +00:00
* fix schema update issue * added test * fix issue for tags and api page * added test for tags page * update test for api collection * remove i18 mock * address pr comments (cherry picked from commit 852fa432c587cc4646acdcfa3c83241155b78569)
This commit is contained in:
parent
745ff15e91
commit
fd37cb9c34
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 { render, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { EntityType, TabSpecificField } from '../../enums/entity.enum';
|
||||
import { Include } from '../../generated/type/include';
|
||||
import { useFqn } from '../../hooks/useFqn';
|
||||
import { getApiCollectionByFQN } from '../../rest/apiCollectionsAPI';
|
||||
import { getApiEndPoints } from '../../rest/apiEndpointsAPI';
|
||||
import { getFeedCounts } from '../../utils/CommonUtils';
|
||||
import APICollectionPage from './APICollectionPage';
|
||||
|
||||
jest.mock('../../rest/apiCollectionsAPI', () => ({
|
||||
getApiCollectionByFQN: jest.fn().mockResolvedValue({}),
|
||||
restoreApiCollection: jest.fn().mockResolvedValue({ version: 1 }),
|
||||
patchApiCollection: jest.fn().mockResolvedValue({}),
|
||||
updateApiCollectionVote: jest.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
jest.mock('../../rest/apiEndpointsAPI', () => ({
|
||||
getApiEndPoints: jest.fn().mockResolvedValue({ paging: { total: 0 } }),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/CommonUtils', () => ({
|
||||
getFeedCounts: jest.fn(),
|
||||
getEntityMissingError: jest.fn(),
|
||||
showErrorToast: jest.fn(),
|
||||
showSuccessToast: jest.fn(),
|
||||
getCountBadge: jest.fn().mockImplementation((count) => <span>{count}</span>),
|
||||
}));
|
||||
|
||||
jest.mock('../../hooks/useFqn', () => ({
|
||||
useFqn: jest.fn().mockReturnValue({ fqn: 'api.collection.v1' }),
|
||||
}));
|
||||
|
||||
jest.mock('../../hooks/useCustomPages', () => ({
|
||||
useCustomPages: jest.fn().mockReturnValue({
|
||||
customizedPage: null,
|
||||
isLoading: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../hooks/useTableFilters', () => ({
|
||||
useTableFilters: jest.fn().mockReturnValue({
|
||||
filters: { showDeletedEndpoints: false },
|
||||
setFilters: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../context/PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: jest.fn().mockReturnValue({
|
||||
getEntityPermissionByFqn: jest.fn().mockResolvedValue({
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn().mockReturnValue({ push: jest.fn() }),
|
||||
useParams: jest
|
||||
.fn()
|
||||
.mockReturnValue({ fqn: 'api.collection.v1', tab: 'api_endpoint' }),
|
||||
useLocation: jest.fn().mockReturnValue({ pathname: '/test' }),
|
||||
}));
|
||||
|
||||
jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () =>
|
||||
jest.fn().mockImplementation(() => <div>ErrorPlaceHolder</div>)
|
||||
);
|
||||
|
||||
jest.mock('../../components/common/Loader/Loader', () =>
|
||||
jest.fn().mockImplementation(() => <div>Loader</div>)
|
||||
);
|
||||
|
||||
jest.mock('../../components/AppRouter/withActivityFeed', () => ({
|
||||
withActivityFeed: jest.fn().mockImplementation((Component) => Component),
|
||||
}));
|
||||
|
||||
jest.mock('../../components/common/DocumentTitle/DocumentTitle', () =>
|
||||
jest.fn().mockImplementation(() => <div>DocumentTitle</div>)
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component',
|
||||
() => ({
|
||||
DataAssetsHeader: jest
|
||||
.fn()
|
||||
.mockImplementation(() => <div>DataAssetsHeader</div>),
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'../../components/Customization/GenericProvider/GenericProvider',
|
||||
() => ({
|
||||
GenericProvider: jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }) => <div>{children}</div>),
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock('../../utils/AdvancedSearchClassBase', () => {
|
||||
const mockAutocomplete = () => async () => ({
|
||||
data: [],
|
||||
paging: { total: 0 },
|
||||
});
|
||||
|
||||
const AdvancedSearchClassBase = Object.assign(
|
||||
jest.fn().mockImplementation(() => ({
|
||||
baseConfig: {
|
||||
types: {
|
||||
multiselect: {
|
||||
widgets: {},
|
||||
},
|
||||
select: {
|
||||
widgets: {
|
||||
text: {
|
||||
operators: ['like', 'not_like', 'regexp'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
{
|
||||
autocomplete: mockAutocomplete,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
AdvancedSearchClassBase,
|
||||
__esModule: true,
|
||||
default: AdvancedSearchClassBase,
|
||||
};
|
||||
});
|
||||
|
||||
describe('APICollectionPage', () => {
|
||||
const renderComponent = () => {
|
||||
return render(<APICollectionPage />);
|
||||
};
|
||||
|
||||
it('should call APIs with updated FQN when FQN changes', async () => {
|
||||
// Set initial FQN
|
||||
(useParams as jest.Mock).mockReturnValue({
|
||||
fqn: 'api.collection.v1',
|
||||
tab: 'api_endpoint',
|
||||
});
|
||||
|
||||
const { rerender } = renderComponent();
|
||||
|
||||
// Verify initial API calls
|
||||
await waitFor(() => {
|
||||
expect(getApiCollectionByFQN).toHaveBeenCalledWith('api.collection.v1', {
|
||||
fields: `${TabSpecificField.OWNERS},${TabSpecificField.TAGS},${TabSpecificField.DOMAIN},${TabSpecificField.VOTES},${TabSpecificField.EXTENSION},${TabSpecificField.DATA_PRODUCTS}`,
|
||||
include: Include.All,
|
||||
});
|
||||
expect(getApiEndPoints).toHaveBeenCalledWith({
|
||||
apiCollection: 'api.collection.v1',
|
||||
service: '',
|
||||
paging: { limit: 0 },
|
||||
include: Include.NonDeleted,
|
||||
});
|
||||
expect(getFeedCounts).toHaveBeenCalledWith(
|
||||
EntityType.API_COLLECTION,
|
||||
'api.collection.v1',
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
// Clear mocks to track new calls
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Change FQN
|
||||
(useParams as jest.Mock).mockReturnValue({
|
||||
fqn: 'api.collection.v2',
|
||||
tab: 'api_endpoint',
|
||||
});
|
||||
(useFqn as jest.Mock).mockReturnValue({ fqn: 'api.collection.v2' });
|
||||
|
||||
// Rerender with new FQN
|
||||
rerender(<APICollectionPage />);
|
||||
|
||||
// Verify APIs are called with new FQN
|
||||
await waitFor(() => {
|
||||
expect(getApiCollectionByFQN).toHaveBeenCalledWith('api.collection.v2', {
|
||||
fields: `${TabSpecificField.OWNERS},${TabSpecificField.TAGS},${TabSpecificField.DOMAIN},${TabSpecificField.VOTES},${TabSpecificField.EXTENSION},${TabSpecificField.DATA_PRODUCTS}`,
|
||||
include: Include.All,
|
||||
});
|
||||
expect(getApiEndPoints).toHaveBeenCalledWith({
|
||||
apiCollection: 'api.collection.v2',
|
||||
service: '',
|
||||
paging: { limit: 0 },
|
||||
include: Include.NonDeleted,
|
||||
});
|
||||
expect(getFeedCounts).toHaveBeenCalledWith(
|
||||
EntityType.API_COLLECTION,
|
||||
'api.collection.v2',
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
// Verify each API was called exactly once with new FQN
|
||||
expect(getApiCollectionByFQN).toHaveBeenCalledTimes(1);
|
||||
expect(getApiEndPoints).toHaveBeenCalledTimes(1);
|
||||
expect(getFeedCounts).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@ -144,13 +144,13 @@ const APICollectionPage: FunctionComponent = () => {
|
||||
setFeedCount(data);
|
||||
}, []);
|
||||
|
||||
const getEntityFeedCount = () => {
|
||||
const getEntityFeedCount = useCallback(() => {
|
||||
getFeedCounts(
|
||||
EntityType.API_COLLECTION,
|
||||
decodedAPICollectionFQN,
|
||||
handleFeedCount
|
||||
);
|
||||
};
|
||||
}, [handleFeedCount, decodedAPICollectionFQN]);
|
||||
|
||||
const fetchAPICollectionDetails = useCallback(async () => {
|
||||
try {
|
||||
@ -350,7 +350,11 @@ const APICollectionPage: FunctionComponent = () => {
|
||||
fetchAPICollectionDetails();
|
||||
getEntityFeedCount();
|
||||
}
|
||||
}, [viewAPICollectionPermission]);
|
||||
}, [
|
||||
viewAPICollectionPermission,
|
||||
fetchAPICollectionDetails,
|
||||
getEntityFeedCount,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (viewAPICollectionPermission && decodedAPICollectionFQN) {
|
||||
|
||||
@ -171,13 +171,13 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
||||
setFeedCount(data);
|
||||
}, []);
|
||||
|
||||
const getEntityFeedCount = () => {
|
||||
const getEntityFeedCount = useCallback(() => {
|
||||
getFeedCounts(
|
||||
EntityType.DATABASE_SCHEMA,
|
||||
decodedDatabaseSchemaFQN,
|
||||
handleFeedCount
|
||||
);
|
||||
};
|
||||
}, [decodedDatabaseSchemaFQN, handleFeedCount]);
|
||||
|
||||
const fetchDatabaseSchemaDetails = useCallback(async () => {
|
||||
try {
|
||||
@ -403,10 +403,14 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
||||
if (viewDatabaseSchemaPermission) {
|
||||
fetchDatabaseSchemaDetails();
|
||||
fetchStoreProcedureCount();
|
||||
|
||||
getEntityFeedCount();
|
||||
}
|
||||
}, [viewDatabaseSchemaPermission]);
|
||||
}, [
|
||||
viewDatabaseSchemaPermission,
|
||||
fetchDatabaseSchemaDetails,
|
||||
fetchStoreProcedureCount,
|
||||
getEntityFeedCount,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTableCount();
|
||||
|
||||
@ -11,12 +11,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { FEED_COUNT_INITIAL_DATA } from '../../constants/entity.constants';
|
||||
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
|
||||
import { getDatabaseSchemaDetailsByFQN } from '../../rest/databaseAPI';
|
||||
import { getStoredProceduresList } from '../../rest/storedProceduresAPI';
|
||||
import { getFeedCounts } from '../../utils/CommonUtils';
|
||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||
import DatabaseSchemaPageComponent from './DatabaseSchemaPage.component';
|
||||
import {
|
||||
@ -322,4 +323,64 @@ describe('Tests for DatabaseSchemaPage', () => {
|
||||
|
||||
expect(await screen.findByText('testSchemaTablesTab')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should refetch data when decodedDatabaseSchemaFQN changes', async () => {
|
||||
const mockUseParams = jest.requireMock('react-router-dom').useParams;
|
||||
mockUseParams.mockReturnValue({
|
||||
fqn: 'sample_data.ecommerce_db.shopify',
|
||||
tab: 'table',
|
||||
});
|
||||
|
||||
(usePermissionProvider as jest.Mock).mockImplementation(() => ({
|
||||
getEntityPermissionByFqn: jest.fn().mockResolvedValue({
|
||||
ViewBasic: true,
|
||||
}),
|
||||
}));
|
||||
|
||||
const { rerender } = render(<DatabaseSchemaPageComponent />);
|
||||
|
||||
// Wait for initial API calls
|
||||
await waitFor(() => {
|
||||
expect(getDatabaseSchemaDetailsByFQN).toHaveBeenCalledWith(
|
||||
'sample_data.ecommerce_db.shopify',
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(getStoredProceduresList).toHaveBeenCalledWith({
|
||||
databaseSchema: 'sample_data.ecommerce_db.shopify',
|
||||
limit: 0,
|
||||
});
|
||||
expect(getFeedCounts).toHaveBeenCalledWith(
|
||||
'databaseSchema',
|
||||
'sample_data.ecommerce_db.shopify',
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockUseParams.mockReturnValue({
|
||||
fqn: 'Glue.default.information_schema',
|
||||
tab: 'table',
|
||||
});
|
||||
|
||||
// Rerender with new FQN
|
||||
rerender(<DatabaseSchemaPageComponent />);
|
||||
|
||||
// API calls should be made again with new FQN
|
||||
await waitFor(() => {
|
||||
expect(getDatabaseSchemaDetailsByFQN).toHaveBeenCalledWith(
|
||||
'Glue.default.information_schema',
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(getStoredProceduresList).toHaveBeenCalledWith({
|
||||
databaseSchema: 'Glue.default.information_schema',
|
||||
limit: 0,
|
||||
});
|
||||
expect(getFeedCounts).toHaveBeenCalledWith(
|
||||
'databaseSchema',
|
||||
'Glue.default.information_schema',
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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 { render, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { useFqn } from '../../hooks/useFqn';
|
||||
import { searchData } from '../../rest/miscAPI';
|
||||
import { getTagByFqn } from '../../rest/tagAPI';
|
||||
import TagPage from './TagPage';
|
||||
|
||||
jest.mock('../../rest/tagAPI', () => ({
|
||||
getTagByFqn: jest.fn().mockResolvedValue({
|
||||
name: 'NonSensitive',
|
||||
fullyQualifiedName: 'PII.NonSensitive',
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../rest/miscAPI', () => ({
|
||||
searchData: jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
hits: {
|
||||
total: { value: 0 },
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../hooks/useFqn', () => ({
|
||||
useFqn: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn().mockReturnValue({ push: jest.fn() }),
|
||||
useParams: jest.fn().mockReturnValue({ fqn: 'PII.NonSensitive' }),
|
||||
useLocation: jest
|
||||
.fn()
|
||||
.mockReturnValue({ pathname: '/tags/PII.NonSensitive' }),
|
||||
}));
|
||||
|
||||
jest.mock('../../context/PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: jest.fn().mockReturnValue({
|
||||
getEntityPermission: jest.fn().mockResolvedValue({
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock(
|
||||
'../../components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider',
|
||||
() => ({
|
||||
useActivityFeedProvider: jest.fn().mockReturnValue({
|
||||
postFeed: jest.fn(),
|
||||
deleteFeed: jest.fn(),
|
||||
updateFeed: jest.fn(),
|
||||
}),
|
||||
__esModule: true,
|
||||
default: 'ActivityFeedProvider',
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'../../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component',
|
||||
() => ({
|
||||
ActivityFeedTab: jest.fn().mockImplementation(() => <>ActivityFeedTab</>),
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => {
|
||||
return jest.fn().mockImplementation(({ children }) => <div>{children}</div>);
|
||||
});
|
||||
|
||||
jest.mock(
|
||||
'../../components/common/TitleBreadcrumb/TitleBreadcrumb.component',
|
||||
() => {
|
||||
return jest.fn().mockImplementation(() => <div>TitleBreadcrumb</div>);
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock('../../components/common/EntityDescription/DescriptionV1', () => {
|
||||
return jest.fn().mockImplementation(() => <div>DescriptionV1</div>);
|
||||
});
|
||||
|
||||
jest.mock('../../components/common/DomainLabel/DomainLabel.component', () => ({
|
||||
DomainLabel: jest.fn().mockImplementation(() => <div>DomainLabel</div>),
|
||||
}));
|
||||
|
||||
jest.mock('../../components/common/ResizablePanels/ResizablePanels', () => {
|
||||
return jest.fn().mockImplementation(({ children }) => <div>{children}</div>);
|
||||
});
|
||||
|
||||
jest.mock(
|
||||
'../../components/Entity/EntityHeader/EntityHeader.component',
|
||||
() => ({
|
||||
EntityHeader: jest.fn().mockImplementation(() => <div>EntityHeader</div>),
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'../../components/Explore/EntitySummaryPanel/EntitySummaryPanel.component',
|
||||
() => {
|
||||
return jest.fn().mockImplementation(() => <div>EntitySummaryPanel</div>);
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'../../components/Glossary/GlossaryTerms/tabs/AssetsTabs.component',
|
||||
() => {
|
||||
return jest.fn().mockImplementation(() => <div>AssetsTabs</div>);
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock('../../components/Modals/EntityDeleteModal/EntityDeleteModal', () => {
|
||||
return jest.fn().mockImplementation(() => <div>EntityDeleteModal</div>);
|
||||
});
|
||||
|
||||
jest.mock(
|
||||
'../../components/Modals/EntityNameModal/EntityNameModal.component',
|
||||
() => {
|
||||
return jest.fn().mockImplementation(() => <div>EntityNameModal</div>);
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock('../../components/Modals/StyleModal/StyleModal.component', () => {
|
||||
return jest.fn().mockImplementation(() => <div>StyleModal</div>);
|
||||
});
|
||||
|
||||
jest.mock(
|
||||
'../../components/DataAssets/AssetsSelectionModal/AssetSelectionModal',
|
||||
() => ({
|
||||
AssetSelectionModal: jest
|
||||
.fn()
|
||||
.mockImplementation(() => <div>AssetSelectionModal</div>),
|
||||
})
|
||||
);
|
||||
|
||||
describe('TagPage', () => {
|
||||
it('should call getTagData and fetchClassificationTagAssets when tagFqn changes', async () => {
|
||||
(useFqn as jest.Mock).mockReturnValue({ fqn: 'PII.NonSensitive' });
|
||||
|
||||
const { rerender } = render(<TagPage />);
|
||||
|
||||
// Verify initial API calls
|
||||
await waitFor(() => {
|
||||
expect(getTagByFqn).toHaveBeenCalledWith('PII.NonSensitive', {
|
||||
fields: 'domain',
|
||||
});
|
||||
expect(searchData).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Change FQN
|
||||
(useFqn as jest.Mock).mockReturnValue({ fqn: 'Certification.Gold' });
|
||||
|
||||
(getTagByFqn as jest.Mock).mockResolvedValueOnce({
|
||||
name: 'Gold',
|
||||
fullyQualifiedName: 'Certification.Gold',
|
||||
});
|
||||
|
||||
rerender(<TagPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getTagByFqn).toHaveBeenCalledWith('Certification.Gold', {
|
||||
fields: 'domain',
|
||||
});
|
||||
expect(searchData).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -566,7 +566,7 @@ const TagPage = () => {
|
||||
useEffect(() => {
|
||||
getTagData();
|
||||
fetchClassificationTagAssets();
|
||||
}, []);
|
||||
}, [tagFqn]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tagItem) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user