mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-30 11:26:23 +00:00
feat: add a domain icon and drop-down for multiple domain names (#23299)
* feat: add domain icon and comma-separated domain names * fix integration test id * Revamp domain render on search card * fix unit test unused props * fix integration test * nit * fix minor unused props * Fix all failing integration test * nit * Fix domain propagation test * Change font size for domain count * fix overflow count number --------- Co-authored-by: Anujkumar Yadav <anujkumaryadav@Anujkumars-MacBook-Pro.local>
This commit is contained in:
parent
9fd34c8f89
commit
76c4e371a9
@ -14,7 +14,12 @@ import { APIRequestContext, expect, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { ServiceTypes } from '../../constant/settings';
|
||||
import { assignDomain, removeDomain, uuid } from '../../utils/common';
|
||||
import {
|
||||
assignDomain,
|
||||
removeDomain,
|
||||
uuid,
|
||||
verifyDomainLinkInCard,
|
||||
} from '../../utils/common';
|
||||
import {
|
||||
addMultiOwner,
|
||||
addOwner,
|
||||
@ -250,17 +255,16 @@ export class DatabaseClass extends EntityClass {
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyDomainChangeInES(page: Page, domain: Domain['responseData']) {
|
||||
// Verify domain change in ES
|
||||
async verifyDomainChangeInES(page: Page, domains: Domain['responseData'][]) {
|
||||
const searchTerm = this.tableResponseData?.['fullyQualifiedName'];
|
||||
await page.getByTestId('searchBox').fill(searchTerm);
|
||||
await page.getByTestId('searchBox').press('Enter');
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByTestId(`table-data-card_${searchTerm}`)
|
||||
.getByTestId('domains-link')
|
||||
).toContainText(domain.displayName);
|
||||
const entityCard = page.getByTestId(`table-data-card_${searchTerm}`);
|
||||
|
||||
for (const domain of domains) {
|
||||
await verifyDomainLinkInCard(entityCard, domain);
|
||||
}
|
||||
|
||||
await page.getByTestId('searchBox').clear();
|
||||
}
|
||||
@ -272,7 +276,7 @@ export class DatabaseClass extends EntityClass {
|
||||
}
|
||||
|
||||
async verifyDomainPropagation(page: Page, domain: Domain['responseData']) {
|
||||
await this.verifyDomainChangeInES(page, domain);
|
||||
await this.verifyDomainChangeInES(page, [domain]);
|
||||
await this.visitEntityPage(page);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Browser, expect, Page, request } from '@playwright/test';
|
||||
import { Browser, expect, Locator, Page, request } from '@playwright/test';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { SidebarItem } from '../constant/sidebar';
|
||||
import { adjectives, nouns } from '../constant/user';
|
||||
@ -411,6 +411,23 @@ export const generateRandomUsername = (prefix = '') => {
|
||||
};
|
||||
};
|
||||
|
||||
export const verifyDomainLinkInCard = async (
|
||||
entityCard: Locator,
|
||||
domain: Domain['responseData']
|
||||
) => {
|
||||
const domainLink = entityCard.getByTestId('domain-link').filter({
|
||||
hasText: domain.displayName,
|
||||
});
|
||||
|
||||
await expect(domainLink).toBeVisible();
|
||||
await expect(domainLink).toContainText(domain.displayName);
|
||||
|
||||
const href = await domainLink.getAttribute('href');
|
||||
|
||||
expect(href).toContain('/domain/');
|
||||
await expect(domainLink).toBeEnabled();
|
||||
};
|
||||
|
||||
export const verifyDomainPropagation = async (
|
||||
page: Page,
|
||||
domain: Domain['responseData'],
|
||||
@ -418,12 +435,16 @@ export const verifyDomainPropagation = async (
|
||||
) => {
|
||||
await page.getByTestId('searchBox').fill(childFqnSearchTerm);
|
||||
await page.getByTestId('searchBox').press('Enter');
|
||||
await page.waitForSelector(`[data-testid*="table-data-card"]`);
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByTestId(`table-data-card_${childFqnSearchTerm}`)
|
||||
.getByTestId('domains-link')
|
||||
).toContainText(domain.displayName);
|
||||
const entityCard = page.getByTestId(`table-data-card_${childFqnSearchTerm}`);
|
||||
|
||||
await expect(entityCard).toBeVisible();
|
||||
|
||||
const domainLink = entityCard.getByTestId('domain-link').first();
|
||||
|
||||
await expect(domainLink).toBeVisible();
|
||||
await expect(domainLink).toContainText(domain.displayName);
|
||||
};
|
||||
|
||||
export const replaceAllSpacialCharWith_ = (text: string) => {
|
||||
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import searchClassBase from '../../../utils/SearchClassBase';
|
||||
import ExploreSearchCard from './ExploreSearchCard';
|
||||
import { ExploreSearchCardProps } from './ExploreSearchCard.interface';
|
||||
|
||||
jest.mock('../../../utils/RouterUtils', () => ({
|
||||
getDomainPath: jest.fn().mockReturnValue('/mock-domain'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/EntityUtils', () => ({
|
||||
getEntityName: jest.fn().mockReturnValue('Mock Entity'),
|
||||
highlightSearchText: jest.fn().mockReturnValue(''),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/SearchClassBase', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
getListOfEntitiesWithoutDomain: jest.fn(),
|
||||
getListOfEntitiesWithoutTier: jest.fn().mockReturnValue([]),
|
||||
getServiceIcon: jest.fn().mockReturnValue(<span>service-icon</span>),
|
||||
getEntityBreadcrumbs: jest.fn().mockReturnValue([]),
|
||||
getEntityIcon: jest.fn().mockReturnValue(<span>entity-icon</span>),
|
||||
getEntityLink: jest.fn().mockReturnValue('/entity/test'),
|
||||
getEntityName: jest.fn().mockReturnValue('Test Domain'),
|
||||
getSearchEntityLinkTarget: jest.fn().mockReturnValue('_self'),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../common/DomainDisplay/DomainDisplay.component', () => ({
|
||||
DomainDisplay: jest
|
||||
.fn()
|
||||
.mockReturnValue(<div data-testid="domain-display">Domain Display</div>),
|
||||
}));
|
||||
|
||||
const baseSource: ExploreSearchCardProps['source'] = {
|
||||
id: 'base-1',
|
||||
fullyQualifiedName: 'test.fqn',
|
||||
name: 'test',
|
||||
entityType: 'table',
|
||||
tags: [],
|
||||
owners: [],
|
||||
domains: [],
|
||||
};
|
||||
|
||||
const defaultProps: Omit<ExploreSearchCardProps, 'source'> = {
|
||||
id: '1',
|
||||
showEntityIcon: false,
|
||||
};
|
||||
|
||||
const renderCard = (
|
||||
sourceOverrides: Partial<ExploreSearchCardProps['source']>
|
||||
) =>
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<ExploreSearchCard
|
||||
{...defaultProps}
|
||||
source={{ ...baseSource, ...sourceOverrides }}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('ExploreSearchCard - Domain section', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders DomainDisplay component', () => {
|
||||
renderCard({
|
||||
domains: [{ id: '1', fullyQualifiedName: 'domain.test', type: 'domain' }],
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('domain-display')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders empty Domain row when no domains exist and entity requires domain', () => {
|
||||
(
|
||||
searchClassBase.getListOfEntitiesWithoutDomain as jest.Mock
|
||||
).mockReturnValue([]);
|
||||
|
||||
renderCard({ domains: [] });
|
||||
|
||||
expect(screen.queryByTestId('domain-icon')).not.toBeInTheDocument();
|
||||
|
||||
expect(screen.getByTestId('Domain')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render Domain when entityType is excluded from domains', () => {
|
||||
(
|
||||
searchClassBase.getListOfEntitiesWithoutDomain as jest.Mock
|
||||
).mockReturnValue(['table']);
|
||||
|
||||
renderCard({ domains: [] });
|
||||
|
||||
expect(screen.queryByText('Domain')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -30,13 +30,13 @@ import { Table } from '../../../generated/entity/data/table';
|
||||
import { EntityReference } from '../../../generated/entity/type';
|
||||
import { TagLabel } from '../../../generated/tests/testCase';
|
||||
import { AssetCertification } from '../../../generated/type/assetCertification';
|
||||
import { getEntityName, highlightSearchText } from '../../../utils/EntityUtils';
|
||||
import { getDomainPath } from '../../../utils/RouterUtils';
|
||||
import { highlightSearchText } from '../../../utils/EntityUtils';
|
||||
import searchClassBase from '../../../utils/SearchClassBase';
|
||||
import { stringToHTML } from '../../../utils/StringsUtils';
|
||||
import { getUsagePercentile } from '../../../utils/TableUtils';
|
||||
import { useRequiredParams } from '../../../utils/useRequiredParams';
|
||||
import CertificationTag from '../../common/CertificationTag/CertificationTag';
|
||||
import { DomainDisplay } from '../../common/DomainDisplay/DomainDisplay.component';
|
||||
import { OwnerLabel } from '../../common/OwnerLabel/OwnerLabel.component';
|
||||
import TitleBreadcrumb from '../../common/TitleBreadcrumb/TitleBreadcrumb.component';
|
||||
import TableDataCardBody from '../../Database/TableDataCardBody/TableDataCardBody';
|
||||
@ -84,14 +84,13 @@ const ExploreSearchCard: React.FC<ExploreSearchCardProps> = forwardRef<
|
||||
);
|
||||
|
||||
const _otherDetails: ExtraInfo[] = [
|
||||
...(source?.domains
|
||||
? source.domains.map((domain) => ({
|
||||
key: 'Domains',
|
||||
value: getDomainPath(domain.fullyQualifiedName) ?? '',
|
||||
placeholderText: getEntityName(domain),
|
||||
isLink: true,
|
||||
openInNewTab: false,
|
||||
}))
|
||||
...(source?.domains && source.domains.length > 0
|
||||
? [
|
||||
{
|
||||
key: 'Domains',
|
||||
value: <DomainDisplay domains={source.domains} />,
|
||||
},
|
||||
]
|
||||
: !searchClassBase
|
||||
.getListOfEntitiesWithoutDomain()
|
||||
.includes(source?.entityType ?? '')
|
||||
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { Dropdown, Typography } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ReactComponent as DomainIcon } from '../../../assets/svg/ic-domain.svg';
|
||||
import { EntityReference } from '../../../generated/entity/type';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { getDomainPath } from '../../../utils/RouterUtils';
|
||||
|
||||
interface DomainDisplayProps {
|
||||
domains: EntityReference[];
|
||||
showIcon?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DomainLink: React.FC<{
|
||||
domain: EntityReference;
|
||||
}> = ({ domain }) => (
|
||||
<>
|
||||
<Link
|
||||
className="no-underline"
|
||||
data-testid="domain-link"
|
||||
to={getDomainPath(domain.fullyQualifiedName) ?? ''}>
|
||||
<Typography.Text className="text-sm text-primary">
|
||||
{getEntityName(domain)}
|
||||
</Typography.Text>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
|
||||
export const DomainDisplay = ({
|
||||
domains,
|
||||
showIcon = true,
|
||||
className = '',
|
||||
}: DomainDisplayProps) => {
|
||||
if (!domains || domains.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (domains.length > 1) {
|
||||
const firstDomain = domains[0];
|
||||
const remainingDomains = domains.slice(1);
|
||||
const remainingCount = remainingDomains.length;
|
||||
|
||||
const dropdownItems = remainingDomains.map((domain, index) => ({
|
||||
key: index,
|
||||
label: (
|
||||
<div className="d-flex items-center gap-2">
|
||||
<DomainIcon height={14} name="domain" width={14} />
|
||||
<Link
|
||||
className="no-underline"
|
||||
to={getDomainPath(domain.fullyQualifiedName) ?? ''}>
|
||||
<Typography.Text className="text-sm text-primary">
|
||||
{getEntityName(domain)}
|
||||
</Typography.Text>
|
||||
</Link>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={`d-flex items-center gap-2 ${className}`}>
|
||||
{showIcon && (
|
||||
<div className="d-flex">
|
||||
<DomainIcon
|
||||
data-testid="domain-icon"
|
||||
height={18}
|
||||
name="domain"
|
||||
width={18}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex items-center gap-2">
|
||||
<DomainLink domain={firstDomain} />
|
||||
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: dropdownItems,
|
||||
className: 'domain-tooltip-list',
|
||||
}}
|
||||
trigger={['hover']}>
|
||||
<Typography.Text
|
||||
className={`flex-center cursor-pointer align-middle ant-typography-secondary domain-count-button ${
|
||||
remainingCount <= 9 ? 'h-6 w-6' : ''
|
||||
}`}
|
||||
data-testid="domain-count-button">
|
||||
<span className="ant-typography domain-count-label">
|
||||
{`+${remainingCount}`}
|
||||
</span>
|
||||
</Typography.Text>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`d-flex items-center gap-2 ${className}`}>
|
||||
{showIcon && (
|
||||
<div className="d-flex">
|
||||
<DomainIcon
|
||||
data-testid="domain-icon"
|
||||
height={18}
|
||||
name="domain"
|
||||
width={18}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex items-center gap-1">
|
||||
<DomainLink domain={domains[0]} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { EntityReference } from '../../../generated/entity/type';
|
||||
import { DomainDisplay } from './DomainDisplay.component';
|
||||
|
||||
jest.mock('../../../utils/EntityUtils', () => ({
|
||||
getEntityName: jest
|
||||
.fn()
|
||||
.mockImplementation((entity) => entity?.name || 'Unknown'),
|
||||
}));
|
||||
|
||||
jest.mock('../../../utils/RouterUtils', () => ({
|
||||
getDomainPath: jest
|
||||
.fn()
|
||||
.mockImplementation((fqn: string) => `/domain/${fqn}`),
|
||||
}));
|
||||
|
||||
jest.mock('../../../assets/svg/ic-domain.svg', () => ({
|
||||
ReactComponent: () => <div data-testid="domain-icon">Domain Icon</div>,
|
||||
}));
|
||||
|
||||
const mockDomain1: EntityReference = {
|
||||
id: 'domain-1',
|
||||
fullyQualifiedName: 'domain.one',
|
||||
name: 'Domain One',
|
||||
type: 'domain',
|
||||
};
|
||||
|
||||
const mockDomain2: EntityReference = {
|
||||
id: 'domain-2',
|
||||
fullyQualifiedName: 'domain.two',
|
||||
name: 'Domain Two',
|
||||
type: 'domain',
|
||||
};
|
||||
|
||||
const mockDomain3: EntityReference = {
|
||||
id: 'domain-3',
|
||||
fullyQualifiedName: 'domain.three',
|
||||
name: 'Domain Three',
|
||||
type: 'domain',
|
||||
};
|
||||
|
||||
const renderDomainDisplay = (props: any) =>
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<DomainDisplay {...props} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('DomainDisplay Component', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render nothing when domains array is empty', () => {
|
||||
const { container } = renderDomainDisplay({ domains: [] });
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it('should render nothing when domains is undefined', () => {
|
||||
const { container } = renderDomainDisplay({ domains: undefined });
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it('should render nothing when domains is null', () => {
|
||||
const { container } = renderDomainDisplay({ domains: null });
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it('should render single domain with icon by default', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1] });
|
||||
|
||||
expect(screen.getByTestId('domain-icon')).toBeInTheDocument();
|
||||
expect(screen.getByText('Domain One')).toBeInTheDocument();
|
||||
expect(screen.getByRole('link')).toHaveAttribute(
|
||||
'href',
|
||||
'/domain/domain.one'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render single domain without icon when showIcon is false', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1], showIcon: false });
|
||||
|
||||
expect(screen.queryByTestId('domain-icon')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Domain One')).toBeInTheDocument();
|
||||
expect(screen.getByRole('link')).toHaveAttribute(
|
||||
'href',
|
||||
'/domain/domain.one'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render multiple domains with dropdown by default', () => {
|
||||
renderDomainDisplay({
|
||||
domains: [mockDomain1, mockDomain2, mockDomain3],
|
||||
});
|
||||
|
||||
expect(screen.getByText('Domain One')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('domain-count-button')).toBeInTheDocument();
|
||||
expect(screen.getByText('+2')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Domain Two')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Domain Three')).not.toBeInTheDocument();
|
||||
expect(screen.getAllByTestId('domain-icon')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should render single domain normally', () => {
|
||||
renderDomainDisplay({
|
||||
domains: [mockDomain1],
|
||||
});
|
||||
|
||||
expect(screen.getByText('Domain One')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('domain-count-button')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(', ')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render correct links for all domains', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1, mockDomain2] });
|
||||
|
||||
expect(screen.getByRole('link', { name: 'Domain One' })).toHaveAttribute(
|
||||
'href',
|
||||
'/domain/domain.one'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle domain with missing fullyQualifiedName', () => {
|
||||
const domainWithoutFQN = {
|
||||
...mockDomain1,
|
||||
fullyQualifiedName: undefined,
|
||||
};
|
||||
|
||||
renderDomainDisplay({ domains: [domainWithoutFQN] });
|
||||
|
||||
expect(screen.getByRole('link')).toHaveAttribute(
|
||||
'href',
|
||||
'/domain/undefined'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle domain with missing name', () => {
|
||||
const domainWithoutName = {
|
||||
...mockDomain1,
|
||||
name: undefined,
|
||||
};
|
||||
|
||||
renderDomainDisplay({ domains: [domainWithoutName] });
|
||||
|
||||
expect(screen.getByText('Unknown')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have proper link accessibility', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1] });
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveAttribute('href', '/domain/domain.one');
|
||||
});
|
||||
|
||||
it('should have proper test IDs for testing', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1] });
|
||||
|
||||
expect(screen.getByTestId('domain-icon')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('domain-link')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should style domain links correctly', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1] });
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
|
||||
expect(link).toHaveClass('no-underline');
|
||||
});
|
||||
|
||||
it('should style domain text correctly', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1] });
|
||||
|
||||
const domainText = screen.getByText('Domain One');
|
||||
|
||||
expect(domainText).toHaveClass('text-sm', 'text-primary');
|
||||
});
|
||||
|
||||
it('should not render icon when showIcon is false', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1], showIcon: false });
|
||||
|
||||
expect(screen.queryByTestId('domain-icon')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render only one icon for multiple domains', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1, mockDomain2, mockDomain3] });
|
||||
|
||||
const domainIcons = screen.getAllByTestId('domain-icon');
|
||||
|
||||
expect(domainIcons).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should handle domain with empty name', () => {
|
||||
const domainWithEmptyName = {
|
||||
...mockDomain1,
|
||||
name: '',
|
||||
};
|
||||
|
||||
renderDomainDisplay({ domains: [domainWithEmptyName] });
|
||||
|
||||
expect(screen.getByText('Unknown')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle domain with empty fullyQualifiedName', () => {
|
||||
const domainWithEmptyFQN = {
|
||||
...mockDomain1,
|
||||
fullyQualifiedName: '',
|
||||
};
|
||||
|
||||
renderDomainDisplay({ domains: [domainWithEmptyFQN] });
|
||||
|
||||
expect(screen.getByRole('link')).toHaveAttribute('href', '/domain/');
|
||||
});
|
||||
|
||||
it('should handle mixed domain data (some with names, some without)', () => {
|
||||
const domainsWithMixedData = [
|
||||
mockDomain1,
|
||||
{ ...mockDomain2, name: undefined },
|
||||
mockDomain3,
|
||||
];
|
||||
|
||||
renderDomainDisplay({ domains: domainsWithMixedData });
|
||||
|
||||
expect(screen.getByText('Domain One')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show correct count in dropdown button', () => {
|
||||
const manyDomains = [
|
||||
mockDomain1,
|
||||
mockDomain2,
|
||||
mockDomain3,
|
||||
mockDomain1,
|
||||
mockDomain2,
|
||||
];
|
||||
|
||||
renderDomainDisplay({
|
||||
domains: manyDomains,
|
||||
});
|
||||
|
||||
expect(screen.getByText('+4')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should always use dropdown behavior for multiple domains', () => {
|
||||
renderDomainDisplay({ domains: [mockDomain1, mockDomain2, mockDomain3] });
|
||||
|
||||
expect(screen.getByText('Domain One')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('domain-count-button')).toBeInTheDocument();
|
||||
expect(screen.getByText('+2')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Domain Two')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -42,14 +42,22 @@
|
||||
|
||||
.domain-count-button {
|
||||
background-color: @primary-button-background;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
padding: 0 4px;
|
||||
border-radius: 200px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
span {
|
||||
color: @blue-24;
|
||||
}
|
||||
border: 1px solid @blue-24;
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.domain-count-label {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user