fix(test): right panel data consumer playwright (#24199)

* (fix)ui: right panel data consumer playwright

* improve panel flow playwright

* fix playwright issue

* fix data consumer owner spec
This commit is contained in:
Harsh Vador 2025-11-07 11:15:09 +05:30 committed by GitHub
parent ba09b9c46e
commit a9edab2ece
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 229 additions and 139 deletions

View File

@ -59,25 +59,24 @@ test.afterAll('Cleanup shared test data', async ({ browser }) => {
await afterAction();
});
async function openEntitySummaryPanel(page: Page, entityType: string) {
await page.getByRole('button', { name: 'Data Assets' }).click();
const dataAssetDropdownRequest = page.waitForResponse(
'/api/v1/search/aggregate?index=dataAsset&field=entityType.keyword*'
);
await page
.getByTestId('drop-down-menu')
.getByTestId('search-input')
.fill(entityType.toLowerCase());
await dataAssetDropdownRequest;
await page.getByTestId(`${entityType.toLowerCase()}-checkbox`).check();
await page.getByTestId('update-btn').click();
async function openEntitySummaryPanel(page: Page, entityName: string) {
const searchResponse = page.waitForResponse('/api/v1/search/query*');
await page.getByTestId('searchBox').fill(entityName);
await searchResponse;
await page.getByTestId('searchBox').press('Enter');
await page.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
await page.waitForLoadState('networkidle');
const firstEntityCard = page
const entityCard = page
.locator('[data-testid="table-data-card"]')
.filter({ hasText: entityName })
.first();
if (await firstEntityCard.isVisible()) {
await firstEntityCard.click();
if (await entityCard.isVisible()) {
await entityCard.click();
await page.waitForLoadState('networkidle');
}
}
@ -93,25 +92,7 @@ async function navigateToExploreAndSelectTable(page: Page) {
.catch(() => {
// Loader might not appear, continue
});
await openEntitySummaryPanel(page, 'table');
// Wait for loader to disappear
await page
.waitForSelector('[data-testid="loader"]', {
state: 'detached',
timeout: 15000,
})
.catch(() => {
// Loader might not appear, continue
});
await page.waitForSelector('.highlight-card', {
timeout: 15000,
});
const summaryPanel = page.locator('.entity-summary-panel-container');
await expect(summaryPanel).toBeVisible({ timeout: 15000 });
await openEntitySummaryPanel(page, testEntity.entity.name);
}
async function waitForPatchResponse(page: Page) {
@ -203,15 +184,12 @@ test.describe('Right Entity Panel - Admin User Flow', () => {
await adminPage
.locator('[data-testid="edit-icon-tier"]')
.scrollIntoViewIfNeeded();
await adminPage.waitForSelector('[data-testid="edit-icon-tier"]', {
state: 'visible',
});
await adminPage.locator('[data-testid="edit-icon-tier"]').click();
const tierCard = adminPage.locator('.ant-popover');
await adminPage.locator('[data-testid="cards"]').scrollIntoViewIfNeeded();
await expect(tierCard).toBeVisible();
await expect(adminPage.locator('[data-testid="cards"]')).toBeVisible();
const tier1Radio = adminPage.getByTestId('radio-btn-Tier1');
await tier1Radio.click();
@ -236,15 +214,16 @@ test.describe('Right Entity Panel - Admin User Flow', () => {
await adminPage
.locator('[data-testid="edit-icon-tags"]')
.scrollIntoViewIfNeeded();
await adminPage.waitForSelector('[data-testid="edit-icon-tags"]', {
state: 'visible',
});
await adminPage.locator('[data-testid="edit-icon-tags"]').click();
const tagsPopover = adminPage.locator('.ant-popover');
await adminPage
.locator('[data-testid="selectable-list"]')
.scrollIntoViewIfNeeded();
await expect(tagsPopover).toBeVisible();
await expect(
adminPage.locator('[data-testid="selectable-list"]')
).toBeVisible();
const tagOption = adminPage.getByTitle('None');
if (await tagOption.isVisible()) {
@ -277,9 +256,13 @@ test.describe('Right Entity Panel - Admin User Flow', () => {
await adminPage.locator('[data-testid="edit-glossary-terms"]').click();
const glossaryPopover = adminPage.locator('.ant-popover');
await adminPage
.locator('[data-testid="selectable-list"]')
.scrollIntoViewIfNeeded();
await expect(glossaryPopover).toBeVisible();
await expect(
adminPage.locator('[data-testid="selectable-list"]')
).toBeVisible();
const firstTerm = adminPage.locator('.ant-list-item').first();
await firstTerm.click();
@ -299,49 +282,73 @@ test.describe('Right Entity Panel - Admin User Flow', () => {
await expect(domainsSection).toBeVisible();
const domainEditButton = domainsSection.locator(
'[data-testid="add-domain"]'
);
if (await domainEditButton.isVisible()) {
await domainEditButton.click();
await domainsSection
.locator('[data-testid="add-domain"]')
.scrollIntoViewIfNeeded();
await adminPage.waitForSelector('[data-testid="add-domain"]', {
state: 'visible',
});
await adminPage.locator('[data-testid="add-domain"]').click();
const tree = adminPage.getByTestId('domain-selectable-tree');
const tree = adminPage.getByTestId('domain-selectable-tree');
await expect(tree).toBeVisible();
await expect(tree).toBeVisible();
const firstNode = tree
.locator('[data-testid="tag-TestDomain"]')
.waitFor({ state: 'visible' });
await firstNode;
await tree.locator('[data-testid="tag-TestDomain"]').click();
const updateBtn = adminPage.getByRole('button', { name: 'Update' });
if (await updateBtn.isVisible()) {
await updateBtn.click();
await waitForPatchResponse(adminPage);
const firstNode = tree
.locator('[data-testid="tag-TestDomain"]')
.waitFor({ state: 'visible' });
await firstNode;
await tree.locator('[data-testid="tag-TestDomain"]').click();
const updateBtn = adminPage.getByRole('button', { name: 'Update' });
if (await updateBtn.isVisible()) {
await updateBtn.click();
await waitForPatchResponse(adminPage);
await expect(
adminPage.getByText(/Domains updated successfully/i)
).toBeVisible();
}
await expect(
adminPage.getByText(/Domains updated successfully/i)
).toBeVisible();
}
});
test('Tab Navigation - Schema Tab', async ({ adminPage }) => {
const summaryPanel = adminPage.locator('.entity-summary-panel-container');
const schemaTab = summaryPanel.getByRole('menuitem', { name: /schema/i });
const schemaTab = adminPage.locator('[data-testid="schema-tab"]');
if (await schemaTab.isVisible()) {
await schemaTab.click();
await adminPage.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
await schemaTab.click();
await adminPage.waitForSelector('[data-testid="loader"]', {
state: 'detached',
});
const tabContent = summaryPanel.locator(
'.entity-summary-panel-tab-content'
const tabContent = adminPage.locator(
'[data-testid="entity-details-section"]'
);
await expect(tabContent).toBeVisible();
testEntity.children.forEach(async (child) => {
const fieldCard = adminPage.locator(
`[data-testid="field-card-${child.name}"]`
);
await expect(tabContent).toBeVisible();
}
await expect(fieldCard).toBeVisible();
const dataTypeBadge = fieldCard.locator(
`[data-testid="data-type-badge-${child.dataType}"]`
);
await expect(dataTypeBadge).toBeVisible();
const fieldName = fieldCard.locator(
`[data-testid="field-name-${child.name}"]`
);
await expect(fieldName).toHaveText(child.name);
const fieldDescription = fieldCard.locator(
`[data-testid="field-description-${child.name}"]`
);
await expect(fieldDescription).toBeVisible();
await expect(fieldDescription).toContainText(child.description);
});
});
test('Tab Navigation - Lineage Tab', async ({ adminPage }) => {
@ -507,9 +514,13 @@ test.describe('Right Entity Panel - Data Steward User Flow', () => {
await dataStewardPage.locator('[data-testid="edit-icon-tier"]').click();
const tierCard = dataStewardPage.locator('.ant-popover');
await dataStewardPage
.locator('[data-testid="cards"]')
.scrollIntoViewIfNeeded();
await expect(tierCard).toBeVisible();
await expect(
dataStewardPage.locator('[data-testid="cards"]')
).toBeVisible();
const tier2Radio = dataStewardPage.getByTestId('radio-btn-Tier2');
await tier2Radio.click();
@ -538,15 +549,20 @@ test.describe('Right Entity Panel - Data Steward User Flow', () => {
await dataStewardPage
.locator('[data-testid="edit-icon-tags"]')
.scrollIntoViewIfNeeded();
await dataStewardPage.waitForSelector('[data-testid="edit-icon-tags"]', {
state: 'visible',
});
await dataStewardPage.locator('[data-testid="edit-icon-tags"]').click();
const tagsPopover = dataStewardPage.locator('.ant-popover');
await dataStewardPage
.locator('[data-testid="selectable-list"]')
.scrollIntoViewIfNeeded();
await expect(tagsPopover).toBeVisible();
await expect(
dataStewardPage.locator('[data-testid="selectable-list"]')
).toBeVisible();
const tagOption = dataStewardPage.getByTitle('PII.Sensitive');
if (await tagOption.isVisible()) {
@ -588,10 +604,17 @@ test.describe('Right Entity Panel - Data Steward User Flow', () => {
.locator('[data-testid="edit-glossary-terms"]')
.click();
const glossaryPopover = dataStewardPage.locator('.ant-popover');
await dataStewardPage
.locator('[data-testid="selectable-list"]')
.scrollIntoViewIfNeeded();
await expect(glossaryPopover).toBeVisible();
await expect(
dataStewardPage.locator('[data-testid="selectable-list"]')
).toBeVisible();
await dataStewardPage.waitForSelector('.ant-list-item', {
state: 'visible',
});
const firstTerm = dataStewardPage.locator('.ant-list-item').first();
await firstTerm.click();
@ -607,13 +630,9 @@ test.describe('Right Entity Panel - Data Steward User Flow', () => {
test('Data Steward - Should NOT have permissions for Domains', async ({
dataStewardPage,
}) => {
const summaryPanel = dataStewardPage.locator(
'.entity-summary-panel-container'
);
await expect(summaryPanel.getByTestId('add-domain')).not.toBeVisible();
await expect(dataStewardPage.getByTestId('add-domain')).not.toBeVisible();
await expect(
summaryPanel.getByTestId('edit-data-products')
dataStewardPage.getByTestId('edit-data-products')
).not.toBeVisible();
});
@ -754,7 +773,7 @@ test.describe('Right Entity Panel - Data Consumer User Flow', () => {
}
});
test('Data Consumer - Should NOT have edit permissions for Owners', async ({
test('Data Consumer - Owners Section - Add and Update', async ({
dataConsumerPage,
}) => {
const summaryPanel = dataConsumerPage.locator(
@ -763,7 +782,36 @@ test.describe('Right Entity Panel - Data Consumer User Flow', () => {
const ownersSection = summaryPanel.locator('.owners-section');
await expect(ownersSection).toBeVisible();
await expect(summaryPanel.getByTestId('edit-owners')).not.toBeVisible();
const editButton = ownersSection.getByTestId('edit-owners');
if (await editButton.isVisible()) {
await editButton.click();
const popover = dataConsumerPage.getByTestId('select-owner-tabs');
await expect(popover).toBeVisible();
await dataConsumerPage.getByRole('tab', { name: 'Users' }).click();
const firstOwner = dataConsumerPage.getByRole('listitem', {
name: 'admin',
exact: true,
});
if (await firstOwner.isVisible()) {
await firstOwner.click();
const updateBtn = dataConsumerPage.getByRole('button', {
name: 'Update',
});
if (await updateBtn.isVisible()) {
await updateBtn.click();
await waitForPatchResponse(dataConsumerPage);
await expect(
dataConsumerPage.getByText(/Owners updated successfully/i)
).toBeVisible();
}
}
}
});
test('Data Consumer - Tier Section - Add and Update', async ({
@ -785,9 +833,13 @@ test.describe('Right Entity Panel - Data Consumer User Flow', () => {
await dataConsumerPage.locator('[data-testid="edit-icon-tier"]').click();
const tierCard = dataConsumerPage.locator('.ant-popover');
await dataConsumerPage
.locator('[data-testid="cards"]')
.scrollIntoViewIfNeeded();
await expect(tierCard).toBeVisible();
await expect(
dataConsumerPage.locator('[data-testid="cards"]')
).toBeVisible();
const tier3Radio = dataConsumerPage.getByTestId('radio-btn-Tier3');
await tier3Radio.click();
@ -822,9 +874,16 @@ test.describe('Right Entity Panel - Data Consumer User Flow', () => {
await dataConsumerPage.locator('[data-testid="edit-icon-tags"]').click();
const tagsPopover = dataConsumerPage.locator('.ant-popover');
await dataConsumerPage.locator('loader').waitFor({
state: 'detached',
});
await dataConsumerPage
.locator('[data-testid="selectable-list"]')
.scrollIntoViewIfNeeded();
await expect(tagsPopover).toBeVisible();
await expect(
dataConsumerPage.locator('[data-testid="selectable-list"]')
).toBeVisible();
const tagOption = dataConsumerPage.getByTitle('PersonalData.Personal');
if (await tagOption.isVisible()) {
@ -868,9 +927,13 @@ test.describe('Right Entity Panel - Data Consumer User Flow', () => {
.locator('[data-testid="edit-glossary-terms"]')
.click();
const glossaryPopover = dataConsumerPage.locator('.ant-popover');
await dataConsumerPage
.locator('[data-testid="selectable-list"]')
.scrollIntoViewIfNeeded();
await expect(glossaryPopover).toBeVisible();
await expect(
dataConsumerPage.locator('[data-testid="selectable-list"]')
).toBeVisible();
const firstTerm = dataConsumerPage.locator('.ant-list-item').first();
await firstTerm.click();

View File

@ -268,19 +268,38 @@ export const DataAssetSummaryPanelV1 = ({
break;
}
};
const { editDomainPermission, editOwnerPermission, editTierPermission } =
useMemo(
() => ({
editDomainPermission: entityPermissions?.EditAll && !dataAsset.deleted,
editOwnerPermission:
(entityPermissions?.EditAll || entityPermissions?.EditOwners) &&
!dataAsset.deleted,
editTierPermission:
(entityPermissions?.EditAll || entityPermissions?.EditTier) &&
!dataAsset.deleted,
}),
[entityPermissions, dataAsset]
);
const {
editDomainPermission,
editOwnerPermission,
editTierPermission,
editTagsPermission,
editDataProductPermission,
editDescriptionPermission,
editGlossaryTermsPermission,
} = useMemo(
() => ({
editDomainPermission: entityPermissions?.EditAll && !dataAsset.deleted,
editDescriptionPermission:
(entityPermissions?.EditAll || entityPermissions?.EditDescription) &&
!dataAsset.deleted,
editGlossaryTermsPermission:
(entityPermissions?.EditGlossaryTerms || entityPermissions?.EditAll) &&
!dataAsset.deleted,
editOwnerPermission:
(entityPermissions?.EditAll || entityPermissions?.EditOwners) &&
!dataAsset.deleted,
editTierPermission:
(entityPermissions?.EditAll || entityPermissions?.EditTier) &&
!dataAsset.deleted,
editTagsPermission:
(entityPermissions?.EditAll || entityPermissions?.EditTags) &&
!dataAsset.deleted,
editDataProductPermission:
entityPermissions?.EditAll && !dataAsset.deleted,
}),
[entityPermissions, dataAsset]
);
const init = useCallback(async () => {
// Do not reset permissions to null when id is temporarily missing during re-renders
if (!dataAsset.id || isTourPage) {
@ -380,9 +399,7 @@ export const DataAssetSummaryPanelV1 = ({
)}
<DescriptionSection
description={dataAsset.description}
hasPermission={
entityPermissions?.EditDescription || entityPermissions?.EditAll
}
hasPermission={editDescriptionPermission}
onDescriptionUpdate={handleDescriptionUpdate}
/>
<OverviewSection
@ -447,7 +464,7 @@ export const DataAssetSummaryPanelV1 = ({
<GlossaryTermsSection
entityId={dataAsset.id}
entityType={entityType}
hasPermission={entityPermissions?.EditGlossaryTerms}
hasPermission={editGlossaryTermsPermission}
key={`glossary-terms-${dataAsset.id}-${
(dataAsset.tags as unknown[])?.length || 0
}`}
@ -460,7 +477,7 @@ export const DataAssetSummaryPanelV1 = ({
<TagsSection
entityId={dataAsset.id}
entityType={entityType}
hasPermission={entityPermissions?.EditTags}
hasPermission={editTagsPermission}
key={`tags-${dataAsset.id}-${
(dataAsset.tags as unknown[])?.length || 0
}`}
@ -476,9 +493,7 @@ export const DataAssetSummaryPanelV1 = ({
dataProducts={dataAsset.dataProducts as EntityReference[]}
entityId={dataAsset.id}
entityType={entityType}
hasPermission={
entityPermissions?.EditAll || entityPermissions?.EditTags
}
hasPermission={editDataProductPermission}
key={`data-products-${dataAsset.id}-${
(dataAsset.dataProducts as unknown[])?.length || 0
}`}
@ -493,9 +508,7 @@ export const DataAssetSummaryPanelV1 = ({
<span className="d-none" data-testid="KnowledgePageSummary" />
<DescriptionSection
description={dataAsset.description}
hasPermission={
entityPermissions?.EditDescription || entityPermissions?.EditAll
}
hasPermission={editDescriptionPermission}
onDescriptionUpdate={handleDescriptionUpdate}
/>
<div>
@ -514,7 +527,7 @@ export const DataAssetSummaryPanelV1 = ({
<TagsSection
entityId={dataAsset.id}
entityType={entityType}
hasPermission={entityPermissions?.EditTags}
hasPermission={editTagsPermission}
key={`tags-${dataAsset.id}-${
(dataAsset.tags as unknown[])?.length || 0
}`}
@ -526,7 +539,7 @@ export const DataAssetSummaryPanelV1 = ({
<GlossaryTermsSection
entityId={dataAsset.id}
entityType={entityType}
hasPermission={entityPermissions?.EditGlossaryTerms}
hasPermission={editGlossaryTermsPermission}
key={`glossary-terms-${dataAsset.id}-${
(dataAsset.tags as unknown[])?.length || 0
}`}
@ -542,9 +555,7 @@ export const DataAssetSummaryPanelV1 = ({
<>
<DescriptionSection
description={dataAsset.description}
hasPermission={
entityPermissions?.EditDescription || entityPermissions?.EditAll
}
hasPermission={editDescriptionPermission}
onDescriptionUpdate={handleDescriptionUpdate}
/>
<div>
@ -587,9 +598,7 @@ export const DataAssetSummaryPanelV1 = ({
<TagsSection
entityId={dataAsset.id}
entityType={entityType}
hasPermission={
entityPermissions?.EditAll || entityPermissions?.EditTags
}
hasPermission={editTagsPermission}
key={`tags-${dataAsset.id}-${
(dataAsset.tags as unknown[])?.length || 0
}`}

View File

@ -39,6 +39,7 @@ const EntityRightPanelVerticalNav: React.FC<EntityRightPanelVerticalNavProps> =
key: EntityRightPanelTab.OVERVIEW,
icon: <ExploreIcon height={16} width={16} />,
label: t('label.overview'),
'data-testid': 'overview-tab',
},
];
@ -48,6 +49,7 @@ const EntityRightPanelVerticalNav: React.FC<EntityRightPanelVerticalNavProps> =
key: EntityRightPanelTab.SCHEMA,
icon: <SchemaIcon height={16} width={16} />,
label: t('label.schema'),
'data-testid': 'schema-tab',
});
}
// Add lineage tab for most entities
@ -56,6 +58,7 @@ const EntityRightPanelVerticalNav: React.FC<EntityRightPanelVerticalNavProps> =
key: EntityRightPanelTab.LINEAGE,
icon: <PlatformLineageIcon height={16} width={16} />,
label: t('label.lineage'),
'data-testid': 'lineage-tab',
});
}
@ -65,6 +68,7 @@ const EntityRightPanelVerticalNav: React.FC<EntityRightPanelVerticalNavProps> =
key: EntityRightPanelTab.DATA_QUALITY,
icon: <DataQualityIcon height={16} width={16} />,
label: t('label.data-quality'),
'data-testid': 'data-quality-tab',
});
}
@ -74,6 +78,7 @@ const EntityRightPanelVerticalNav: React.FC<EntityRightPanelVerticalNavProps> =
key: EntityRightPanelTab.CUSTOM_PROPERTIES,
icon: <CustomPropertiesIcon height={16} width={16} />,
label: t('label.custom-property'),
'data-testid': 'custom-properties-tab',
});
}

View File

@ -230,7 +230,6 @@ const DomainsSection: React.FC<DomainsSectionProps> = ({
showEditButton &&
hasPermission && (
<DomainSelectableList
multiple
hasPermission={hasPermission}
overlayClassName="domain-popover"
popoverProps={{

View File

@ -40,7 +40,13 @@ const EntityDetailsSection: React.FC<EntityDetailsSectionProps> = ({
return null;
}
return <div className="entity-details-section">{entityDetails}</div>;
return (
<div
className="entity-details-section"
data-testid="entity-details-section">
{entityDetails}
</div>
);
};
export default EntityDetailsSection;

View File

@ -40,9 +40,12 @@ const FieldCard: React.FC<FieldCardProps> = ({
return (
<div
className={`field-card ${isHighlighted ? 'field-card-highlighted' : ''}`}>
<div className="field-card-header">
<Badge className="data-type-badge">
className={`field-card ${isHighlighted ? 'field-card-highlighted' : ''}`}
data-testid={`field-card-${fieldName}`}>
<div className="field-card-header" data-testid="field-card-header">
<Badge
className="data-type-badge"
data-testid={`data-type-badge-${dataType}`}>
{getDataTypeString(startCase(dataType))}
</Badge>
<div className="field-name-container">
@ -57,14 +60,19 @@ const FieldCard: React.FC<FieldCardProps> = ({
})}
</span>
)}
<Typography.Text strong className="field-name">
<Typography.Text
strong
className="field-name"
data-testid={`field-name-${fieldName}`}>
{fieldName}
</Typography.Text>
</div>
</div>
<div className="field-card-content">
<Paragraph className="field-description">
<div className="field-card-content" data-testid="field-card-content">
<Paragraph
className="field-description"
data-testid={`field-description-${fieldName}`}>
{description ? (
<RichTextEditorPreviewerV1
markdown={description}

View File

@ -124,7 +124,7 @@ const TagsSectionV1: React.FC<TagsSectionProps> = ({
<TagSelectableList
hasPermission={hasPermission}
popoverProps={{
placement: 'bottomLeft',
placement: 'topRight',
open: popoverOpen,
onOpenChange: handlePopoverOpenChange,
overlayClassName: 'tag-select-popover',