mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-27 23:48:19 +00:00
parent
f6b72d262d
commit
8746058d83
@ -1241,7 +1241,7 @@ export const removeOwner = (entity, isGlossaryPage) => {
|
||||
cy.get('[data-testid="edit-owner"]').click();
|
||||
verifyResponseStatusCode('@getUsers', 200);
|
||||
cy.get("[data-testid='select-owner-tabs']").should('be.visible');
|
||||
cy.get('[data-testid="remove-owner"]').click();
|
||||
cy.get('[data-testid="remove-owner"]').scrollIntoView().click();
|
||||
verifyResponseStatusCode('@patchOwner', 200);
|
||||
if (isGlossaryPage) {
|
||||
cy.get('[data-testid="glossary-owner-name"] > [data-testid="Add"]').should(
|
||||
|
||||
@ -28,34 +28,6 @@ describe('Data Insight settings page should work properly', () => {
|
||||
verifyResponseStatusCode('@getApplications', 200);
|
||||
});
|
||||
|
||||
it('Deploy & run application', () => {
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/apps/name/DataInsightsApplication?fields=*',
|
||||
'getDataInsightDetails'
|
||||
);
|
||||
interceptURL(
|
||||
'POST',
|
||||
'/api/v1/apps/deploy/DataInsightsApplication',
|
||||
'deploy'
|
||||
);
|
||||
interceptURL(
|
||||
'POST',
|
||||
'/api/v1/apps/trigger/DataInsightsApplication',
|
||||
'triggerPipeline'
|
||||
);
|
||||
cy.get(
|
||||
'[data-testid="data-insights-application-card"] [data-testid="config-btn"]'
|
||||
).click();
|
||||
verifyResponseStatusCode('@getDataInsightDetails', 200);
|
||||
cy.get('[data-testid="deploy-button"]').click();
|
||||
verifyResponseStatusCode('@deploy', 200);
|
||||
cy.reload();
|
||||
verifyResponseStatusCode('@getDataInsightDetails', 200);
|
||||
cy.get('[data-testid="run-now-button"]').click();
|
||||
verifyResponseStatusCode('@triggerPipeline', 200);
|
||||
});
|
||||
|
||||
it('Edit data insight application', () => {
|
||||
interceptURL(
|
||||
'GET',
|
||||
@ -115,6 +87,7 @@ describe('Data Insight settings page should work properly', () => {
|
||||
cy.get('[data-testid="save-button"]').click();
|
||||
cy.get('#cronType').click();
|
||||
cy.get('[title="Day"]').click();
|
||||
cy.get('[data-testid="cron-type"]').should('contain', 'Day');
|
||||
cy.get('[data-testid="deploy-button"]').click();
|
||||
verifyResponseStatusCode('@installApplication', 201);
|
||||
verifyResponseStatusCode('@getApplications', 200);
|
||||
@ -122,4 +95,32 @@ describe('Data Insight settings page should work properly', () => {
|
||||
'be.visible'
|
||||
);
|
||||
});
|
||||
|
||||
it('Deploy & run application', () => {
|
||||
interceptURL(
|
||||
'GET',
|
||||
'/api/v1/apps/name/DataInsightsApplication?fields=*',
|
||||
'getDataInsightDetails'
|
||||
);
|
||||
interceptURL(
|
||||
'POST',
|
||||
'/api/v1/apps/deploy/DataInsightsApplication',
|
||||
'deploy'
|
||||
);
|
||||
interceptURL(
|
||||
'POST',
|
||||
'/api/v1/apps/trigger/DataInsightsApplication',
|
||||
'triggerPipeline'
|
||||
);
|
||||
cy.get(
|
||||
'[data-testid="data-insights-application-card"] [data-testid="config-btn"]'
|
||||
).click();
|
||||
verifyResponseStatusCode('@getDataInsightDetails', 200);
|
||||
cy.get('[data-testid="deploy-button"]').click();
|
||||
verifyResponseStatusCode('@deploy', 200);
|
||||
cy.reload();
|
||||
verifyResponseStatusCode('@getDataInsightDetails', 200);
|
||||
cy.get('[data-testid="run-now-button"]').click();
|
||||
verifyResponseStatusCode('@triggerPipeline', 200);
|
||||
});
|
||||
});
|
||||
|
||||
@ -39,6 +39,7 @@ import { domainTypeTooltipDataRender } from '../../../utils/DomainUtils';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { generateFormFields, getField } from '../../../utils/formUtils';
|
||||
import { checkPermission } from '../../../utils/PermissionsUtils';
|
||||
import { UserTeam } from '../../common/AssigneeList/AssigneeList.interface';
|
||||
import '../domain.less';
|
||||
import { DomainFormType } from '../DomainPage.interface';
|
||||
import { AddDomainFormProps } from './AddDomainForm.interface';
|
||||
@ -243,7 +244,8 @@ const AddDomainForm = ({
|
||||
{selectedOwner && (
|
||||
<div className="m-b-sm" data-testid="owner-container">
|
||||
<UserTag
|
||||
id={selectedOwner.id}
|
||||
id={selectedOwner.name ?? selectedOwner.id}
|
||||
isTeam={selectedOwner.type === UserTeam.Team}
|
||||
name={getEntityName(selectedOwner)}
|
||||
size={UserTagSize.small}
|
||||
/>
|
||||
@ -260,7 +262,7 @@ const AddDomainForm = ({
|
||||
size={[8, 8]}>
|
||||
{expertsList.map((d) => (
|
||||
<UserTag
|
||||
id={d.id}
|
||||
id={d.name ?? d.id}
|
||||
key={'expert' + d.id}
|
||||
name={getEntityName(d)}
|
||||
size={UserTagSize.small}
|
||||
|
||||
@ -30,6 +30,7 @@ import {
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { generateFormFields, getField } from '../../../utils/formUtils';
|
||||
import { useAuthContext } from '../../Auth/AuthProviders/AuthProvider';
|
||||
import { UserTeam } from '../../common/AssigneeList/AssigneeList.interface';
|
||||
import ResizablePanels from '../../common/ResizablePanels/ResizablePanels';
|
||||
import TitleBreadcrumb from '../../common/TitleBreadcrumb/TitleBreadcrumb.component';
|
||||
import { UserTag } from '../../common/UserTag/UserTag.component';
|
||||
@ -241,7 +242,8 @@ const AddGlossary = ({
|
||||
{selectedOwner && (
|
||||
<div className="m-y-xs" data-testid="owner-container">
|
||||
<UserTag
|
||||
id={selectedOwner.id}
|
||||
id={selectedOwner.name ?? selectedOwner.id}
|
||||
isTeam={selectedOwner.type === UserTeam.Team}
|
||||
name={getEntityName(selectedOwner)}
|
||||
size={UserTagSize.small}
|
||||
/>
|
||||
@ -258,7 +260,7 @@ const AddGlossary = ({
|
||||
size={[8, 8]}>
|
||||
{reviewersList.map((d, index) => (
|
||||
<UserTag
|
||||
id={d.id}
|
||||
id={d.name ?? d.id}
|
||||
key={index}
|
||||
name={getEntityName(d)}
|
||||
size={UserTagSize.small}
|
||||
|
||||
@ -31,6 +31,7 @@ import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { generateFormFields, getField } from '../../../utils/formUtils';
|
||||
import { fetchGlossaryList } from '../../../utils/TagsUtils';
|
||||
import { useAuthContext } from '../../Auth/AuthProviders/AuthProvider';
|
||||
import { UserTeam } from '../../common/AssigneeList/AssigneeList.interface';
|
||||
import { UserTag } from '../../common/UserTag/UserTag.component';
|
||||
import { UserTagSize } from '../../common/UserTag/UserTag.interface';
|
||||
import { AddGlossaryTermFormProps } from './AddGlossaryTermForm.interface';
|
||||
@ -326,6 +327,7 @@ const AddGlossaryTermForm = ({
|
||||
type: FieldTypes.USER_MULTI_SELECT,
|
||||
props: {
|
||||
hasPermission: true,
|
||||
filterCurrentUser: true,
|
||||
popoverProps: { placement: 'topLeft' },
|
||||
children: (
|
||||
<Button
|
||||
@ -432,7 +434,8 @@ const AddGlossaryTermForm = ({
|
||||
{owner && (
|
||||
<div className="m-y-sm" data-testid="owner-container">
|
||||
<UserTag
|
||||
id={owner.id}
|
||||
id={owner.name ?? owner.id}
|
||||
isTeam={owner.type === UserTeam.Team}
|
||||
name={getEntityName(owner)}
|
||||
size={UserTagSize.small}
|
||||
/>
|
||||
@ -445,7 +448,7 @@ const AddGlossaryTermForm = ({
|
||||
<Space wrap data-testid="reviewers-container" size={[8, 8]}>
|
||||
{reviewersList.map((d) => (
|
||||
<UserTag
|
||||
id={d.id}
|
||||
id={d.name ?? d.id}
|
||||
key={d.id}
|
||||
name={getEntityName(d)}
|
||||
size={UserTagSize.small}
|
||||
|
||||
@ -57,6 +57,14 @@ const GlossaryDetails = ({
|
||||
const [isDescriptionEditable, setIsDescriptionEditable] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const getEntityFeedCount = () => {
|
||||
getFeedCounts(
|
||||
EntityType.GLOSSARY,
|
||||
glossary.fullyQualifiedName ?? '',
|
||||
setFeedCount
|
||||
);
|
||||
};
|
||||
|
||||
const handleGlossaryUpdate = async (updatedGlossary: Glossary) => {
|
||||
await updateGlossary(updatedGlossary);
|
||||
getEntityFeedCount();
|
||||
@ -114,14 +122,6 @@ const GlossaryDetails = ({
|
||||
[glossary, isVersionView]
|
||||
);
|
||||
|
||||
const getEntityFeedCount = () => {
|
||||
getFeedCounts(
|
||||
EntityType.GLOSSARY,
|
||||
glossary.fullyQualifiedName ?? '',
|
||||
setFeedCount
|
||||
);
|
||||
};
|
||||
|
||||
const handleTabChange = (activeKey: string) => {
|
||||
if (activeKey !== activeTab) {
|
||||
history.push(
|
||||
|
||||
@ -43,7 +43,6 @@ import { useEntityExportModalProvider } from '../../../components/Entity/EntityE
|
||||
import { EntityHeader } from '../../../components/Entity/EntityHeader/EntityHeader.component';
|
||||
import EntityDeleteModal from '../../../components/Modals/EntityDeleteModal/EntityDeleteModal';
|
||||
import EntityNameModal from '../../../components/Modals/EntityNameModal/EntityNameModal.component';
|
||||
import { OperationPermission } from '../../../components/PermissionProvider/PermissionProvider.interface';
|
||||
import Voting from '../../../components/Voting/Voting.component';
|
||||
import { VotingDataProps } from '../../../components/Voting/voting.interface';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||
@ -76,19 +75,7 @@ import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import { useAuthContext } from '../../Auth/AuthProviders/AuthProvider';
|
||||
import { TitleBreadcrumbProps } from '../../common/TitleBreadcrumb/TitleBreadcrumb.interface';
|
||||
import StyleModal from '../../Modals/StyleModal/StyleModal.component';
|
||||
|
||||
export interface GlossaryHeaderProps {
|
||||
isVersionView?: boolean;
|
||||
supportAddOwner?: boolean;
|
||||
selectedData: Glossary | GlossaryTerm;
|
||||
permissions: OperationPermission;
|
||||
isGlossary: boolean;
|
||||
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onAssetAdd?: () => void;
|
||||
updateVote?: (data: VotingDataProps) => Promise<void>;
|
||||
onAddGlossaryTerm: (glossaryTerm: GlossaryTerm | undefined) => void;
|
||||
}
|
||||
import { GlossaryHeaderProps } from './GlossaryHeader.interface';
|
||||
|
||||
const GlossaryHeader = ({
|
||||
selectedData,
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2023 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 { Glossary } from '../../../generated/entity/data/glossary';
|
||||
import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm';
|
||||
import { OperationPermission } from '../../PermissionProvider/PermissionProvider.interface';
|
||||
import { VotingDataProps } from '../../Voting/voting.interface';
|
||||
|
||||
export interface GlossaryHeaderProps {
|
||||
isVersionView?: boolean;
|
||||
supportAddOwner?: boolean;
|
||||
permissions: OperationPermission;
|
||||
selectedData: Glossary | GlossaryTerm;
|
||||
isGlossary: boolean;
|
||||
onUpdate: (data: GlossaryTerm | Glossary) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onAssetAdd?: () => void;
|
||||
updateVote?: (data: VotingDataProps) => Promise<void>;
|
||||
onAddGlossaryTerm: (glossaryTerm: GlossaryTerm | undefined) => void;
|
||||
}
|
||||
@ -182,7 +182,7 @@ export const AddEditPersonaForm = ({
|
||||
size={[8, 8]}>
|
||||
{usersList.map((d) => (
|
||||
<UserTag
|
||||
id={d.id}
|
||||
id={d.name ?? d.id}
|
||||
key={d.id}
|
||||
name={getEntityName(d)}
|
||||
size={UserTagSize.small}
|
||||
|
||||
@ -241,7 +241,6 @@ export const SelectableList = ({
|
||||
removeMargin
|
||||
placeholder={searchPlaceholder ?? t('label.search')}
|
||||
searchBarDataTestId={searchBarDataTestId}
|
||||
searchValue={searchText}
|
||||
typingInterval={500}
|
||||
onSearch={handleSearch}
|
||||
/>
|
||||
@ -279,10 +278,7 @@ export const SelectableList = ({
|
||||
{customTagRenderer ? (
|
||||
customTagRenderer(item)
|
||||
) : (
|
||||
<UserTag
|
||||
id={item.name ?? ''}
|
||||
name={item.displayName ?? item.name ?? ''}
|
||||
/>
|
||||
<UserTag id={item.name ?? ''} name={getEntityName(item)} />
|
||||
)}
|
||||
</List.Item>
|
||||
)}
|
||||
|
||||
@ -27,6 +27,7 @@ import { searchData } from '../../../rest/miscAPI';
|
||||
import { getUsers } from '../../../rest/userAPI';
|
||||
import { formatUsersResponse } from '../../../utils/APIUtils';
|
||||
import { getEntityReferenceListFromEntities } from '../../../utils/EntityUtils';
|
||||
import { useAuthContext } from '../../Auth/AuthProviders/AuthProvider';
|
||||
import { SelectableList } from '../SelectableList/SelectableList.component';
|
||||
import './user-select-dropdown.less';
|
||||
import { UserSelectableListProps } from './UserSelectableList.interface';
|
||||
@ -38,9 +39,11 @@ export const UserSelectableList = ({
|
||||
children,
|
||||
popoverProps,
|
||||
multiSelect = true,
|
||||
filterCurrentUser = false,
|
||||
}: UserSelectableListProps) => {
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const { currentUser } = useAuthContext();
|
||||
|
||||
const fetchOptions = async (searchText: string, after?: string) => {
|
||||
if (searchText) {
|
||||
@ -60,6 +63,13 @@ export const UserSelectableList = ({
|
||||
EntityType.USER
|
||||
);
|
||||
|
||||
if (filterCurrentUser) {
|
||||
const user = data.find((user) => user.id === currentUser?.id);
|
||||
if (user) {
|
||||
data.splice(data.indexOf(user), 1);
|
||||
}
|
||||
}
|
||||
|
||||
return { data, paging: { total: res.data.hits.total.value } };
|
||||
} catch (error) {
|
||||
return { data: [], paging: { total: 0 } };
|
||||
@ -75,6 +85,12 @@ export const UserSelectableList = ({
|
||||
data,
|
||||
EntityType.USER
|
||||
);
|
||||
if (filterCurrentUser) {
|
||||
const user = filterData.find((user) => user.id === currentUser?.id);
|
||||
if (user) {
|
||||
filterData.splice(filterData.indexOf(user), 1);
|
||||
}
|
||||
}
|
||||
|
||||
return { data: filterData, paging };
|
||||
} catch (error) {
|
||||
|
||||
@ -20,6 +20,7 @@ export type UserSelectableListProps =
|
||||
selectedUsers: EntityReference[];
|
||||
children?: ReactNode;
|
||||
popoverProps?: PopoverProps;
|
||||
filterCurrentUser?: boolean;
|
||||
} & (
|
||||
| {
|
||||
multiSelect?: true;
|
||||
|
||||
@ -10,17 +10,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { UserSelectableList } from './UserSelectableList.component';
|
||||
|
||||
const mockOnUpdate = jest.fn();
|
||||
|
||||
jest.mock('../../../rest/userAPI', () => ({
|
||||
getUsers: jest.fn().mockResolvedValue({ data: [], paging: { total: 5 } }),
|
||||
}));
|
||||
|
||||
jest.mock('../../../rest/miscAPI', () => ({
|
||||
searchData: jest.fn().mockResolvedValue({ data: [], paging: { total: 5 } }),
|
||||
}));
|
||||
|
||||
describe('SelectableList Component Test', () => {
|
||||
jest.mock('../SelectableList/SelectableList.component', () => ({
|
||||
SelectableList: jest.fn().mockReturnValue(<div>selectable-list</div>),
|
||||
}));
|
||||
|
||||
describe('UserSelectableList Component Test', () => {
|
||||
it('should render disabled button if no permission', () => {
|
||||
render(
|
||||
<UserSelectableList
|
||||
@ -34,4 +42,34 @@ describe('SelectableList Component Test', () => {
|
||||
expect(screen.getByTestId('add-user')).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render enabled button if has permission', () => {
|
||||
render(
|
||||
<UserSelectableList
|
||||
hasPermission
|
||||
selectedUsers={[]}
|
||||
onUpdate={mockOnUpdate}
|
||||
/>
|
||||
);
|
||||
|
||||
act(() => {
|
||||
expect(screen.getByTestId('add-user')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render selectablelist if click on add-user', () => {
|
||||
render(
|
||||
<UserSelectableList
|
||||
hasPermission
|
||||
selectedUsers={[]}
|
||||
onUpdate={mockOnUpdate}
|
||||
/>
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(screen.getByTestId('add-user'));
|
||||
});
|
||||
|
||||
expect(screen.getByText('selectable-list')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@ -28,6 +28,7 @@ export const UserTag = ({
|
||||
bordered,
|
||||
size = UserTagSize.default,
|
||||
className,
|
||||
isTeam = false,
|
||||
}: UserTags) => {
|
||||
if (isUndefined(id) && isUndefined(name)) {
|
||||
return null;
|
||||
@ -58,7 +59,7 @@ export const UserTag = ({
|
||||
)}
|
||||
data-testid="user-tag"
|
||||
size={8}>
|
||||
<ProfilePicture name={id} width={toString(width[size])} />
|
||||
<ProfilePicture isTeam={isTeam} name={id} width={toString(width[size])} />
|
||||
<Typography.Text className={fontSizes[size]}>{name}</Typography.Text>
|
||||
{closable && <CloseOutlined size={width[size]} onClick={onRemove} />}
|
||||
</Space>
|
||||
|
||||
@ -18,6 +18,7 @@ export interface UserTags {
|
||||
bordered?: boolean;
|
||||
size?: UserTagSize;
|
||||
className?: string;
|
||||
isTeam?: boolean;
|
||||
}
|
||||
|
||||
export enum UserTagSize {
|
||||
|
||||
@ -11,9 +11,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { UserTag } from './UserTag.component';
|
||||
import { UserTagSize } from './UserTag.interface';
|
||||
|
||||
jest.mock('../ProfilePicture/ProfilePicture', () => {
|
||||
return jest.fn().mockReturnValue(<div>ProfilePicture</div>);
|
||||
@ -42,4 +43,35 @@ describe('UserTag Component', () => {
|
||||
expect(queryByTestId('user-tag')).not.toBeInTheDocument();
|
||||
expect(container).toHaveTextContent('');
|
||||
});
|
||||
|
||||
const userTagProps = {
|
||||
id: '123',
|
||||
name: 'John Doe',
|
||||
onRemove: jest.fn(),
|
||||
closable: true,
|
||||
bordered: true,
|
||||
size: UserTagSize.default,
|
||||
className: 'custom-class',
|
||||
isTeam: false,
|
||||
};
|
||||
|
||||
it('renders without crashing', () => {
|
||||
render(<UserTag {...userTagProps} />);
|
||||
|
||||
// Add more specific assertions if needed
|
||||
expect(screen.getByTestId('user-tag')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onRemove when close icon is clicked', () => {
|
||||
render(<UserTag {...userTagProps} />);
|
||||
const closeIcon = screen
|
||||
.getByTestId('user-tag')
|
||||
.querySelector('.anticon-close');
|
||||
|
||||
// Simulate click on the close icon
|
||||
closeIcon && fireEvent.click(closeIcon);
|
||||
|
||||
// Check if the onRemove callback is called
|
||||
expect(userTagProps.onRemove).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user