mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-07 13:07:22 +00:00
Fix: UI Should show other tags if the glossary search index fails. (#5081)
This commit is contained in:
parent
e23bb5d0f4
commit
3ee279fa70
@ -20,6 +20,7 @@ import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'
|
||||
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
||||
import { getTeamAndUserDetailsPath } from '../../constants/constants';
|
||||
import { observerOptions } from '../../constants/Mydata.constants';
|
||||
import { SettledStatus } from '../../enums/axios.enum';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { OwnerType } from '../../enums/user.enum';
|
||||
import { Dashboard } from '../../generated/entity/data/dashboard';
|
||||
@ -358,24 +359,38 @@ const DashboardDetails = ({
|
||||
|
||||
const fetchTagsAndGlossaryTerms = () => {
|
||||
setIsTagLoading(true);
|
||||
Promise.all([getTagCategories(), fetchGlossaryTerms()])
|
||||
Promise.allSettled([getTagCategories(), fetchGlossaryTerms()])
|
||||
.then((values) => {
|
||||
let tagsAndTerms: TagOption[] = [];
|
||||
if (values[0].data) {
|
||||
tagsAndTerms = getTaglist(values[0].data).map((tag) => {
|
||||
if (
|
||||
values[0].status === SettledStatus.FULFILLED &&
|
||||
values[0].value.data
|
||||
) {
|
||||
tagsAndTerms = getTaglist(values[0].value.data).map((tag) => {
|
||||
return { fqn: tag, source: 'Tag' };
|
||||
});
|
||||
}
|
||||
if (values[1] && values[1].length > 0) {
|
||||
const glossaryTerms: TagOption[] = getGlossaryTermlist(values[1]).map(
|
||||
(tag) => {
|
||||
return { fqn: tag, source: 'Glossary' };
|
||||
}
|
||||
);
|
||||
if (
|
||||
values[1].status === SettledStatus.FULFILLED &&
|
||||
values[1].value &&
|
||||
values[1].value.length > 0
|
||||
) {
|
||||
const glossaryTerms: TagOption[] = getGlossaryTermlist(
|
||||
values[1].value
|
||||
).map((tag) => {
|
||||
return { fqn: tag, source: 'Glossary' };
|
||||
});
|
||||
tagsAndTerms = [...tagsAndTerms, ...glossaryTerms];
|
||||
}
|
||||
setTagList(tagsAndTerms);
|
||||
setTagFetchFailed(false);
|
||||
if (
|
||||
values[0].status === SettledStatus.FULFILLED &&
|
||||
values[1].status === SettledStatus.FULFILLED
|
||||
) {
|
||||
setTagFetchFailed(false);
|
||||
} else {
|
||||
setTagFetchFailed(true);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setTagList([]);
|
||||
@ -577,6 +592,7 @@ const DashboardDetails = ({
|
||||
</td>
|
||||
<td
|
||||
className="tw-group tw-relative tableBody-cell"
|
||||
data-testid="tags-wrapper"
|
||||
onClick={() => {
|
||||
if (!editChartTags) {
|
||||
// Fetch tags and terms only once
|
||||
|
||||
@ -11,16 +11,31 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { findByTestId, findByText, render } from '@testing-library/react';
|
||||
import { LeafNodes, LoadingNodeState } from 'Models';
|
||||
import {
|
||||
findByTestId,
|
||||
findByText,
|
||||
fireEvent,
|
||||
render,
|
||||
} from '@testing-library/react';
|
||||
import { flatten } from 'lodash';
|
||||
import {
|
||||
FormattedGlossaryTermData,
|
||||
LeafNodes,
|
||||
LoadingNodeState,
|
||||
TagOption,
|
||||
} from 'Models';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Dashboard } from '../../generated/entity/data/dashboard';
|
||||
import { TagCategory, TagClass } from '../../generated/entity/tags/tagCategory';
|
||||
import { EntityLineage } from '../../generated/type/entityLineage';
|
||||
import { EntityReference } from '../../generated/type/entityReference';
|
||||
import { Paging } from '../../generated/type/paging';
|
||||
import { TagLabel } from '../../generated/type/tagLabel';
|
||||
import { fetchGlossaryTerms } from '../../utils/GlossaryUtils';
|
||||
import { getTagCategories } from '../../utils/TagsUtils';
|
||||
import DashboardDetails from './DashboardDetails.component';
|
||||
import { ChartType } from './DashboardDetails.interface';
|
||||
|
||||
jest.mock('../../authentication/auth-provider/AuthProvider', () => {
|
||||
return {
|
||||
@ -54,7 +69,13 @@ const mockUserTeam = [
|
||||
];
|
||||
|
||||
const DashboardDetailsProps = {
|
||||
charts: [],
|
||||
charts: [
|
||||
{
|
||||
chartUrl: 'http://localhost',
|
||||
chartType: 'Area',
|
||||
displayName: 'Test chart',
|
||||
},
|
||||
] as ChartType[],
|
||||
serviceType: '',
|
||||
dashboardUrl: '',
|
||||
tagList: [],
|
||||
@ -100,6 +121,58 @@ const DashboardDetailsProps = {
|
||||
const mockObserve = jest.fn();
|
||||
const mockunObserve = jest.fn();
|
||||
|
||||
const mockTagList = [
|
||||
{
|
||||
id: 'tagCatId1',
|
||||
name: 'TagCat1',
|
||||
description: '',
|
||||
categoryType: 'Classification',
|
||||
children: [
|
||||
{
|
||||
id: 'tagId1',
|
||||
name: 'Tag1',
|
||||
fullyQualifiedName: 'TagCat1.Tag1',
|
||||
description: '',
|
||||
deprecated: false,
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'tagCatId2',
|
||||
name: 'TagCat2',
|
||||
description: '',
|
||||
categoryType: 'Classification',
|
||||
children: [
|
||||
{
|
||||
id: 'tagId2',
|
||||
name: 'Tag2',
|
||||
fullyQualifiedName: 'TagCat2.Tag2',
|
||||
description: '',
|
||||
deprecated: false,
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const mockGlossaryList = [
|
||||
{
|
||||
name: 'Tag1',
|
||||
displayName: 'Tag1',
|
||||
fqdn: 'Glossary.Tag1',
|
||||
type: 'glossaryTerm',
|
||||
id: 'glossaryTagId1',
|
||||
},
|
||||
{
|
||||
name: 'Tag2',
|
||||
displayName: 'Tag2',
|
||||
fqdn: 'Glossary.Tag2',
|
||||
type: 'glossaryTerm',
|
||||
id: 'glossaryTagId2',
|
||||
},
|
||||
];
|
||||
|
||||
window.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
||||
observe: mockObserve,
|
||||
unobserve: mockunObserve,
|
||||
@ -117,7 +190,15 @@ jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => {
|
||||
});
|
||||
|
||||
jest.mock('../tags-container/tags-container', () => {
|
||||
return jest.fn().mockReturnValue(<p>Tag Container</p>);
|
||||
return jest.fn().mockImplementation(({ tagList }) => {
|
||||
return (
|
||||
<>
|
||||
{tagList.map((tag: TagOption, idx: number) => (
|
||||
<p key={idx}>{tag.fqn}</p>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
jest.mock('../tags/tags', () => {
|
||||
@ -144,6 +225,14 @@ jest.mock('../EntityLineage/EntityLineage.component', () => {
|
||||
return jest.fn().mockReturnValue(<p data-testid="lineage">Lineage</p>);
|
||||
});
|
||||
|
||||
jest.mock('../common/non-admin-action/NonAdminAction', () => {
|
||||
return jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }) => (
|
||||
<p data-testid="tag-action">{children}</p>
|
||||
));
|
||||
});
|
||||
|
||||
jest.mock('../../utils/CommonUtils', () => ({
|
||||
addToRecentViewed: jest.fn(),
|
||||
getCountBadge: jest.fn(),
|
||||
@ -154,6 +243,30 @@ jest.mock('../../utils/CommonUtils', () => ({
|
||||
getEntityPlaceHolder: jest.fn().mockReturnValue('value'),
|
||||
getEntityName: jest.fn().mockReturnValue('entityName'),
|
||||
pluralize: jest.fn().mockReturnValue('2 charts'),
|
||||
isEven: jest.fn().mockReturnValue(true),
|
||||
getEntityDeleteMessage: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/GlossaryUtils', () => ({
|
||||
fetchGlossaryTerms: jest.fn(() => Promise.resolve(mockGlossaryList)),
|
||||
getGlossaryTermlist: jest.fn((terms) => {
|
||||
return terms.map((term: FormattedGlossaryTermData) => term?.fqdn);
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/TagsUtils', () => ({
|
||||
getTagCategories: jest.fn(() => Promise.resolve({ data: mockTagList })),
|
||||
getTaglist: jest.fn((categories) => {
|
||||
const children = categories.map((category: TagCategory) => {
|
||||
return category.children || [];
|
||||
});
|
||||
const allChildren = flatten(children);
|
||||
const tagList = (allChildren as unknown as TagClass[]).map((tag) => {
|
||||
return tag?.fullyQualifiedName || '';
|
||||
});
|
||||
|
||||
return tagList;
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('Test DashboardDetails component', () => {
|
||||
@ -243,4 +356,100 @@ describe('Test DashboardDetails component', () => {
|
||||
|
||||
expect(mockObserve).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Check if tags and glossary-terms are present', async () => {
|
||||
const { getByTestId, findByText } = render(
|
||||
<DashboardDetails {...DashboardDetailsProps} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = await findByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = await findByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toBeInTheDocument();
|
||||
expect(glossaryTerm1).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Check if only tags are present', async () => {
|
||||
(fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getByTestId, findByText, queryByText } = render(
|
||||
<DashboardDetails {...DashboardDetailsProps} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = await findByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = queryByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toBeInTheDocument();
|
||||
expect(glossaryTerm1).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Check if only glossary terms are present', async () => {
|
||||
(getTagCategories as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getByTestId, findByText, queryByText } = render(
|
||||
<DashboardDetails {...DashboardDetailsProps} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = queryByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = await findByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).not.toBeInTheDocument();
|
||||
expect(glossaryTerm1).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Check that tags and glossary terms are not present', async () => {
|
||||
(getTagCategories as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
(fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getByTestId, queryByText } = render(
|
||||
<DashboardDetails {...DashboardDetailsProps} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = queryByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = queryByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).not.toBeInTheDocument();
|
||||
expect(glossaryTerm1).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@ -21,6 +21,7 @@ import { Link } from 'react-router-dom';
|
||||
import { useExpanded, useTable } from 'react-table';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
||||
import { getTableDetailsPath } from '../../constants/constants';
|
||||
import { SettledStatus } from '../../enums/axios.enum';
|
||||
import { EntityType, FqnPart } from '../../enums/entity.enum';
|
||||
import {
|
||||
Column,
|
||||
@ -154,24 +155,38 @@ const EntityTable = ({
|
||||
|
||||
const fetchTagsAndGlossaryTerms = () => {
|
||||
setIsTagLoading(true);
|
||||
Promise.all([getTagCategories(), fetchGlossaryTerms()])
|
||||
Promise.allSettled([getTagCategories(), fetchGlossaryTerms()])
|
||||
.then((values) => {
|
||||
let tagsAndTerms: TagOption[] = [];
|
||||
if (values[0].data) {
|
||||
tagsAndTerms = getTaglist(values[0].data).map((tag) => {
|
||||
if (
|
||||
values[0].status === SettledStatus.FULFILLED &&
|
||||
values[0].value.data
|
||||
) {
|
||||
tagsAndTerms = getTaglist(values[0].value.data).map((tag) => {
|
||||
return { fqn: tag, source: 'Tag' };
|
||||
});
|
||||
}
|
||||
if (values[1] && values[1].length > 0) {
|
||||
const glossaryTerms: TagOption[] = getGlossaryTermlist(values[1]).map(
|
||||
(tag) => {
|
||||
return { fqn: tag, source: 'Glossary' };
|
||||
}
|
||||
);
|
||||
if (
|
||||
values[1].status === SettledStatus.FULFILLED &&
|
||||
values[1].value &&
|
||||
values[1].value.length > 0
|
||||
) {
|
||||
const glossaryTerms: TagOption[] = getGlossaryTermlist(
|
||||
values[1].value
|
||||
).map((tag) => {
|
||||
return { fqn: tag, source: 'Glossary' };
|
||||
});
|
||||
tagsAndTerms = [...tagsAndTerms, ...glossaryTerms];
|
||||
}
|
||||
setAllTags(tagsAndTerms);
|
||||
setTagFetchFailed(false);
|
||||
if (
|
||||
values[0].status === SettledStatus.FULFILLED &&
|
||||
values[1].status === SettledStatus.FULFILLED
|
||||
) {
|
||||
setTagFetchFailed(false);
|
||||
} else {
|
||||
setTagFetchFailed(true);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setAllTags([]);
|
||||
@ -525,6 +540,7 @@ const EntityTable = ({
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
data-testid="tags-wrapper"
|
||||
onClick={() => {
|
||||
if (!editColumnTag) {
|
||||
handleEditColumnTag(row.original, row.id);
|
||||
|
||||
@ -18,10 +18,15 @@ import {
|
||||
queryByTestId,
|
||||
render,
|
||||
} from '@testing-library/react';
|
||||
import { flatten } from 'lodash';
|
||||
import { FormattedGlossaryTermData, TagOption } from 'Models';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Table } from '../../generated/entity/data/table';
|
||||
import { TagCategory, TagClass } from '../../generated/entity/tags/tagCategory';
|
||||
import { ModifiedTableColumn } from '../../interface/dataQuality.interface';
|
||||
import { fetchGlossaryTerms } from '../../utils/GlossaryUtils';
|
||||
import { getTagCategories } from '../../utils/TagsUtils';
|
||||
import EntityTable from './EntityTable.component';
|
||||
|
||||
const mockTableheader = [
|
||||
@ -144,12 +149,68 @@ const mockEntityTableProp = {
|
||||
onThreadLinkSelect,
|
||||
};
|
||||
|
||||
const mockTagList = [
|
||||
{
|
||||
id: 'tagCatId1',
|
||||
name: 'TagCat1',
|
||||
description: '',
|
||||
categoryType: 'Classification',
|
||||
children: [
|
||||
{
|
||||
id: 'tagId1',
|
||||
name: 'Tag1',
|
||||
fullyQualifiedName: 'TagCat1.Tag1',
|
||||
description: '',
|
||||
deprecated: false,
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'tagCatId2',
|
||||
name: 'TagCat2',
|
||||
description: '',
|
||||
categoryType: 'Classification',
|
||||
children: [
|
||||
{
|
||||
id: 'tagId2',
|
||||
name: 'Tag2',
|
||||
fullyQualifiedName: 'TagCat2.Tag2',
|
||||
description: '',
|
||||
deprecated: false,
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const mockGlossaryList = [
|
||||
{
|
||||
name: 'Tag1',
|
||||
displayName: 'Tag1',
|
||||
fqdn: 'Glossary.Tag1',
|
||||
type: 'glossaryTerm',
|
||||
id: 'glossaryTagId1',
|
||||
},
|
||||
{
|
||||
name: 'Tag2',
|
||||
displayName: 'Tag2',
|
||||
fqdn: 'Glossary.Tag2',
|
||||
type: 'glossaryTerm',
|
||||
id: 'glossaryTagId2',
|
||||
},
|
||||
];
|
||||
|
||||
jest.mock('@fortawesome/react-fontawesome', () => ({
|
||||
FontAwesomeIcon: jest.fn().mockReturnValue(<i>Icon</i>),
|
||||
}));
|
||||
|
||||
jest.mock('../common/non-admin-action/NonAdminAction', () => {
|
||||
return jest.fn().mockReturnValue(<p>NonAdminAction</p>);
|
||||
return jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }) => (
|
||||
<p data-testid="tag-action">{children}</p>
|
||||
));
|
||||
});
|
||||
|
||||
jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => {
|
||||
@ -159,7 +220,15 @@ jest.mock('../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor', () => ({
|
||||
ModalWithMarkdownEditor: jest.fn().mockReturnValue(<p>EditorModal</p>),
|
||||
}));
|
||||
jest.mock('../tags-container/tags-container', () => {
|
||||
return jest.fn().mockReturnValue(<p>TagContainer</p>);
|
||||
return jest.fn().mockImplementation(({ tagList }) => {
|
||||
return (
|
||||
<>
|
||||
{tagList.map((tag: TagOption, idx: number) => (
|
||||
<p key={idx}>{tag.fqn}</p>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
});
|
||||
});
|
||||
jest.mock('../tags-viewer/tags-viewer', () => {
|
||||
return jest.fn().mockReturnValue(<p>TagViewer</p>);
|
||||
@ -168,6 +237,28 @@ jest.mock('../tags/tags', () => {
|
||||
return jest.fn().mockReturnValue(<p>Tag</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../utils/GlossaryUtils', () => ({
|
||||
fetchGlossaryTerms: jest.fn(() => Promise.resolve(mockGlossaryList)),
|
||||
getGlossaryTermlist: jest.fn((terms) => {
|
||||
return terms.map((term: FormattedGlossaryTermData) => term?.fqdn);
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/TagsUtils', () => ({
|
||||
getTagCategories: jest.fn(() => Promise.resolve({ data: mockTagList })),
|
||||
getTaglist: jest.fn((categories) => {
|
||||
const children = categories.map((category: TagCategory) => {
|
||||
return category.children || [];
|
||||
});
|
||||
const allChildren = flatten(children);
|
||||
const tagList = (allChildren as unknown as TagClass[]).map((tag) => {
|
||||
return tag?.fullyQualifiedName || '';
|
||||
});
|
||||
|
||||
return tagList;
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('Test EntityTable Component', () => {
|
||||
it('Check if it has all child elements', async () => {
|
||||
const { container } = render(<EntityTable {...mockEntityTableProp} />, {
|
||||
@ -309,4 +400,97 @@ describe('Test EntityTable Component', () => {
|
||||
String(mockEntityFieldThreads[0].count)
|
||||
);
|
||||
});
|
||||
|
||||
it('Check if tags and glossary-terms are present', async () => {
|
||||
const { getAllByTestId, findAllByText } = render(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getAllByTestId('tags-wrapper')[0];
|
||||
fireEvent.click(tagWrapper);
|
||||
|
||||
const tag1 = await findAllByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = await findAllByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toHaveLength(mockEntityTableProp.tableColumns.length);
|
||||
expect(glossaryTerm1).toHaveLength(mockEntityTableProp.tableColumns.length);
|
||||
});
|
||||
|
||||
it('Check if only tags are present', async () => {
|
||||
(fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getAllByTestId, findAllByText, queryAllByText } = render(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getAllByTestId('tags-wrapper')[0];
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = await findAllByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = queryAllByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toHaveLength(mockEntityTableProp.tableColumns.length);
|
||||
expect(glossaryTerm1).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('Check if only glossary terms are present', async () => {
|
||||
(getTagCategories as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getAllByTestId, findAllByText, queryAllByText } = render(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getAllByTestId('tags-wrapper')[0];
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = queryAllByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = await findAllByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toHaveLength(0);
|
||||
expect(glossaryTerm1).toHaveLength(mockEntityTableProp.tableColumns.length);
|
||||
});
|
||||
|
||||
it('Check that tags and glossary terms are not present', async () => {
|
||||
(getTagCategories as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
(fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getAllByTestId, queryAllByText } = render(
|
||||
<EntityTable {...mockEntityTableProp} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getAllByTestId('tags-wrapper')[0];
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = queryAllByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = queryAllByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toHaveLength(0);
|
||||
expect(glossaryTerm1).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@ -18,9 +18,17 @@ import {
|
||||
queryByTestId,
|
||||
render,
|
||||
} from '@testing-library/react';
|
||||
import { flatten } from 'lodash';
|
||||
import { FormattedGlossaryTermData, TagOption } from 'Models';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import {
|
||||
TagCategory,
|
||||
TagClass,
|
||||
} from '../../../generated/entity/tags/tagCategory';
|
||||
import { TagLabel } from '../../../generated/type/tagLabel';
|
||||
import { fetchGlossaryTerms } from '../../../utils/GlossaryUtils';
|
||||
import { getTagCategories } from '../../../utils/TagsUtils';
|
||||
import EntityPageInfo from './EntityPageInfo';
|
||||
|
||||
const mockEntityFieldThreads = [
|
||||
@ -112,6 +120,58 @@ const mockEntityInfoProp = {
|
||||
onThreadLinkSelect,
|
||||
};
|
||||
|
||||
const mockTagList = [
|
||||
{
|
||||
id: 'tagCatId1',
|
||||
name: 'TagCat1',
|
||||
description: '',
|
||||
categoryType: 'Classification',
|
||||
children: [
|
||||
{
|
||||
id: 'tagId1',
|
||||
name: 'Tag1',
|
||||
fullyQualifiedName: 'TagCat1.Tag1',
|
||||
description: '',
|
||||
deprecated: false,
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'tagCatId2',
|
||||
name: 'TagCat2',
|
||||
description: '',
|
||||
categoryType: 'Classification',
|
||||
children: [
|
||||
{
|
||||
id: 'tagId2',
|
||||
name: 'Tag2',
|
||||
fullyQualifiedName: 'TagCat2.Tag2',
|
||||
description: '',
|
||||
deprecated: false,
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const mockGlossaryList = [
|
||||
{
|
||||
name: 'Tag1',
|
||||
displayName: 'Tag1',
|
||||
fqdn: 'Glossary.Tag1',
|
||||
type: 'glossaryTerm',
|
||||
id: 'glossaryTagId1',
|
||||
},
|
||||
{
|
||||
name: 'Tag2',
|
||||
displayName: 'Tag2',
|
||||
fqdn: 'Glossary.Tag2',
|
||||
type: 'glossaryTerm',
|
||||
id: 'glossaryTagId2',
|
||||
},
|
||||
];
|
||||
|
||||
jest.mock('../../../utils/CommonUtils', () => ({
|
||||
getHtmlForNonAdminAction: jest.fn(),
|
||||
}));
|
||||
@ -122,8 +182,10 @@ jest.mock('../../../utils/EntityUtils', () => ({
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/GlossaryUtils', () => ({
|
||||
fetchGlossaryTerms: jest.fn(),
|
||||
getGlossaryTermlist: jest.fn(),
|
||||
fetchGlossaryTerms: jest.fn(() => Promise.resolve(mockGlossaryList)),
|
||||
getGlossaryTermlist: jest.fn((terms) => {
|
||||
return terms.map((term: FormattedGlossaryTermData) => term?.fqdn);
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/TableUtils', () => ({
|
||||
@ -131,18 +193,38 @@ jest.mock('../../../utils/TableUtils', () => ({
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/TagsUtils', () => ({
|
||||
getTagCategories: jest.fn(),
|
||||
getTaglist: jest.fn(),
|
||||
getTagCategories: jest.fn(() => Promise.resolve({ data: mockTagList })),
|
||||
getTaglist: jest.fn((categories) => {
|
||||
const children = categories.map((category: TagCategory) => {
|
||||
return category.children || [];
|
||||
});
|
||||
const allChildren = flatten(children);
|
||||
const tagList = (allChildren as unknown as TagClass[]).map((tag) => {
|
||||
return tag?.fullyQualifiedName || '';
|
||||
});
|
||||
|
||||
return tagList;
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../non-admin-action/NonAdminAction', () => {
|
||||
return jest
|
||||
.fn()
|
||||
.mockReturnValue(<p data-testid="tag-action">NonAdminAction</p>);
|
||||
.mockImplementation(({ children }) => (
|
||||
<p data-testid="tag-action">{children}</p>
|
||||
));
|
||||
});
|
||||
|
||||
jest.mock('../../tags-container/tags-container', () => {
|
||||
return jest.fn().mockReturnValue(<p>TagContainer</p>);
|
||||
return jest.fn().mockImplementation(({ tagList }) => {
|
||||
return (
|
||||
<>
|
||||
{tagList.map((tag: TagOption, idx: number) => (
|
||||
<p key={idx}>{tag.fqn}</p>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
jest.mock('../../tags-viewer/tags-viewer', () => {
|
||||
@ -431,4 +513,100 @@ describe('Test EntityPageInfo component', () => {
|
||||
|
||||
expect(onThreadLinkSelect).toBeCalled();
|
||||
});
|
||||
|
||||
it('Check if tags and glossary-terms are present', async () => {
|
||||
const { getByTestId, findByText } = render(
|
||||
<EntityPageInfo {...mockEntityInfoProp} isTagEditable />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = await findByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = await findByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toBeInTheDocument();
|
||||
expect(glossaryTerm1).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Check if only tags are present', async () => {
|
||||
(fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getByTestId, findByText, queryByText } = render(
|
||||
<EntityPageInfo {...mockEntityInfoProp} isTagEditable />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = await findByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = queryByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).toBeInTheDocument();
|
||||
expect(glossaryTerm1).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Check if only glossary terms are present', async () => {
|
||||
(getTagCategories as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getByTestId, findByText, queryByText } = render(
|
||||
<EntityPageInfo {...mockEntityInfoProp} isTagEditable />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = queryByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = await findByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).not.toBeInTheDocument();
|
||||
expect(glossaryTerm1).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Check that tags and glossary terms are not present', async () => {
|
||||
(getTagCategories as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
(fetchGlossaryTerms as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.reject()
|
||||
);
|
||||
const { getByTestId, queryByText } = render(
|
||||
<EntityPageInfo {...mockEntityInfoProp} isTagEditable />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const tagWrapper = getByTestId('tags-wrapper');
|
||||
fireEvent.click(
|
||||
tagWrapper,
|
||||
new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
const tag1 = queryByText('TagCat1.Tag1');
|
||||
const glossaryTerm1 = queryByText('Glossary.Tag1');
|
||||
|
||||
expect(tag1).not.toBeInTheDocument();
|
||||
expect(glossaryTerm1).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@ -19,6 +19,7 @@ import { EntityFieldThreads, EntityTags, ExtraInfo, TagOption } from 'Models';
|
||||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||
import { FOLLOWERS_VIEW_CAP } from '../../../constants/constants';
|
||||
import { SettledStatus } from '../../../enums/axios.enum';
|
||||
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
|
||||
import { EntityReference } from '../../../generated/type/entityReference';
|
||||
import { LabelType, State, TagLabel } from '../../../generated/type/tagLabel';
|
||||
@ -223,24 +224,38 @@ const EntityPageInfo = ({
|
||||
|
||||
const fetchTagsAndGlossaryTerms = () => {
|
||||
setIsTagLoading(true);
|
||||
Promise.all([getTagCategories(), fetchGlossaryTerms()])
|
||||
Promise.allSettled([getTagCategories(), fetchGlossaryTerms()])
|
||||
.then((values) => {
|
||||
let tagsAndTerms: TagOption[] = [];
|
||||
if (values[0].data) {
|
||||
tagsAndTerms = getTaglist(values[0].data).map((tag) => {
|
||||
if (
|
||||
values[0].status === SettledStatus.FULFILLED &&
|
||||
values[0].value.data
|
||||
) {
|
||||
tagsAndTerms = getTaglist(values[0].value.data).map((tag) => {
|
||||
return { fqn: tag, source: 'Tag' };
|
||||
});
|
||||
}
|
||||
if (values[1] && values[1].length > 0) {
|
||||
const glossaryTerms: TagOption[] = getGlossaryTermlist(values[1]).map(
|
||||
(tag) => {
|
||||
return { fqn: tag, source: 'Glossary' };
|
||||
}
|
||||
);
|
||||
if (
|
||||
values[1].status === SettledStatus.FULFILLED &&
|
||||
values[1].value &&
|
||||
values[1].value.length > 0
|
||||
) {
|
||||
const glossaryTerms: TagOption[] = getGlossaryTermlist(
|
||||
values[1].value
|
||||
).map((tag) => {
|
||||
return { fqn: tag, source: 'Glossary' };
|
||||
});
|
||||
tagsAndTerms = [...tagsAndTerms, ...glossaryTerms];
|
||||
}
|
||||
setTagList(tagsAndTerms);
|
||||
setTagFetchFailed(false);
|
||||
if (
|
||||
values[0].status === SettledStatus.FULFILLED &&
|
||||
values[1].status === SettledStatus.FULFILLED
|
||||
) {
|
||||
setTagFetchFailed(false);
|
||||
} else {
|
||||
setTagFetchFailed(true);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setTagList([]);
|
||||
@ -395,6 +410,7 @@ const EntityPageInfo = ({
|
||||
trigger="click">
|
||||
<div
|
||||
className="tw-inline-block"
|
||||
data-testid="tags-wrapper"
|
||||
onClick={() => {
|
||||
// Fetch tags and terms only once
|
||||
if (tagList.length === 0 || tagFetchFailed) {
|
||||
|
||||
@ -18,3 +18,8 @@ export enum ClientErrors {
|
||||
FORBIDDEN = 403,
|
||||
NOT_FOUND = 404,
|
||||
}
|
||||
|
||||
export enum SettledStatus {
|
||||
FULFILLED = 'fulfilled',
|
||||
REJECTED = 'rejected',
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user