fix lineage playwright (#17889)

* fix lineage playwright

* remove in operator usage and use lodash get utility

* fix metric playwright

* fix lineage pw

* update drag and drop

* fix flakiness

* minor sonar fix

---------

Co-authored-by: Sachin Chaurasiya <sachinchaurasiyachotey87@gmail.com>
This commit is contained in:
Karan Hotchandani 2024-09-30 10:54:39 +05:30 committed by GitHub
parent 41d94ac068
commit 58ed12cf47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 217 additions and 205 deletions

View File

@ -34,6 +34,7 @@ import {
connectEdgeBetweenNodes,
deleteEdge,
deleteNode,
editLineage,
performZoomOut,
removeColumnLineage,
setupEntitiesForLineage,
@ -52,6 +53,7 @@ const entities = [
MlModelClass,
ContainerClass,
SearchIndexClass,
ApiEndpointClass,
MetricClass,
] as const;
@ -72,21 +74,23 @@ test.afterAll('Cleanup', async ({ browser }) => {
for (const EntityClass of entities) {
const defaultEntity = new EntityClass();
test.fixme(
`Lineage creation from ${defaultEntity.getType()} entity`,
async ({ browser }) => {
test(`Lineage creation from ${defaultEntity.getType()} entity`, async ({
browser,
}) => {
test.slow(true);
const { page } = await createNewPage(browser);
const { currentEntity, entities, cleanup } =
await setupEntitiesForLineage(page, defaultEntity);
const { currentEntity, entities, cleanup } = await setupEntitiesForLineage(
page,
defaultEntity
);
await test.step('Should create lineage for the entity', async () => {
await redirectToHomePage(page);
await currentEntity.visitEntityPage(page);
await visitLineageTab(page);
await verifyColumnLayerInactive(page);
await page.click('[data-testid="edit-lineage"]');
await editLineage(page);
await performZoomOut(page);
for (const entity of entities) {
await connectEdgeBetweenNodes(page, currentEntity, entity);
@ -105,7 +109,7 @@ for (const EntityClass of entities) {
});
await test.step('Should create pipeline between entities', async () => {
await page.click('[data-testid="edit-lineage"]');
await editLineage(page);
await performZoomOut(page);
for (const entity of entities) {
@ -113,27 +117,23 @@ for (const EntityClass of entities) {
}
});
await test.step(
'Remove lineage between nodes for the entity',
async () => {
await test.step('Remove lineage between nodes for the entity', async () => {
await redirectToHomePage(page);
await currentEntity.visitEntityPage(page);
await visitLineageTab(page);
await page.click('[data-testid="edit-lineage"]');
await editLineage(page);
await performZoomOut(page);
for (const entity of entities) {
await deleteEdge(page, currentEntity, entity);
}
}
);
});
await cleanup();
}
);
});
}
test.fixme('Verify column lineage between tables', async ({ browser }) => {
test('Verify column lineage between tables', async ({ browser }) => {
const { page } = await createNewPage(browser);
const { apiContext, afterAction } = await getApiContext(page);
const table1 = new TableClass();
@ -171,9 +171,7 @@ test.fixme('Verify column lineage between tables', async ({ browser }) => {
await afterAction();
});
test.fixme(
'Verify column lineage between table and topic',
async ({ browser }) => {
test('Verify column lineage between table and topic', async ({ browser }) => {
const { page } = await createNewPage(browser);
const { apiContext, afterAction } = await getApiContext(page);
const table = new TableClass();
@ -206,12 +204,11 @@ test.fixme(
await topic.delete(apiContext);
await afterAction();
}
);
});
test.fixme(
'Verify column lineage between topic and api endpoint',
async ({ browser }) => {
test('Verify column lineage between topic and api endpoint', async ({
browser,
}) => {
const { page } = await createNewPage(browser);
const { apiContext, afterAction } = await getApiContext(page);
const topic = new TopicClass();
@ -245,12 +242,11 @@ test.fixme(
await apiEndpoint.delete(apiContext);
await afterAction();
}
);
});
test.fixme(
'Verify column lineage between table and api endpoint',
async ({ browser }) => {
test('Verify column lineage between table and api endpoint', async ({
browser,
}) => {
const { page } = await createNewPage(browser);
const { apiContext, afterAction } = await getApiContext(page);
const table = new TableClass();
@ -283,5 +279,4 @@ test.fixme(
await apiEndpoint.delete(apiContext);
await afterAction();
}
);
});

View File

@ -37,12 +37,20 @@ export const activateColumnLayer = async (page: Page) => {
await page.click('[data-testid="lineage-layer-column-btn"]');
};
export const editLineage = async (page: Page) => {
await page.click('[data-testid="edit-lineage"]');
await expect(
page.getByTestId('table_search_index-draggable-icon')
).toBeVisible();
};
export const performZoomOut = async (page: Page) => {
for (let i = 0; i < 5; i++) {
const zoomOutBtn = page.locator('.react-flow__controls-zoomout');
const enabled = await zoomOutBtn.isEnabled();
if (enabled) {
zoomOutBtn.dispatchEvent('click');
for (const _ of Array.from({ length: 8 })) {
await zoomOutBtn.dispatchEvent('click');
}
}
};
@ -78,6 +86,20 @@ export const deleteEdge = async (
await deleteRes;
};
export const dragAndDropNode = async (
page: Page,
originSelector: string,
destinationSelector: string
) => {
const destinationElement = await page.waitForSelector(destinationSelector);
await page.hover(originSelector);
await page.mouse.down();
const box = (await destinationElement.boundingBox())!;
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await destinationElement.hover();
await page.mouse.up();
};
export const dragConnection = async (
page: Page,
sourceId: string,
@ -109,10 +131,10 @@ export const connectEdgeBetweenNodes = async (
const toNodeName = get(toNode, 'entityResponseData.name');
const toNodeFqn = get(toNode, 'entityResponseData.fullyQualifiedName');
await page.locator(`[data-testid="${type}-draggable-icon"]`).hover();
await page.mouse.down();
await page.locator('[data-testid="lineage-details"]').hover();
await page.mouse.up();
const source = `[data-testid="${type}-draggable-icon"]`;
const target = '[data-testid="lineage-details"]';
await dragAndDropNode(page, source, target);
await page.locator('[data-testid="suggestion-node"]').dispatchEvent('click');
@ -370,7 +392,7 @@ export const addPipelineBetweenNodes = async (
) => {
await sourceEntity.visitEntityPage(page);
await page.click('[data-testid="lineage"]');
await page.click('[data-testid="edit-lineage"]');
await editLineage(page);
await performZoomOut(page);

View File

@ -14,7 +14,7 @@ import Icon from '@ant-design/icons';
import { Button, Col, Divider, Row, Space, Tooltip, Typography } from 'antd';
import ButtonGroup from 'antd/lib/button/button-group';
import { AxiosError } from 'axios';
import { capitalize, isEmpty } from 'lodash';
import { capitalize, get, isEmpty } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
@ -161,18 +161,18 @@ export const DataAssetsHeader = ({
const [isBreadcrumbLoading, setIsBreadcrumbLoading] = useState(false);
const [isFollowingLoading, setIsFollowingLoading] = useState(false);
const history = useHistory();
const icon = useMemo(
() =>
'serviceType' in dataAsset ? (
const icon = useMemo(() => {
const serviceType = get(dataAsset, 'serviceType', '');
return serviceType ? (
<img
className="h-9"
src={serviceUtilClassBase.getServiceTypeLogo(
dataAsset as SearchSourceAlias
)}
/>
) : null,
[dataAsset]
);
) : null;
}, [dataAsset]);
const [copyTooltip, setCopyTooltip] = useState<string>();
const excludeEntityService = useMemo(

View File

@ -13,7 +13,7 @@
import Icon from '@ant-design/icons/lib/components/Icon';
import { Button, Col, Divider, Row, Space, Tooltip, Typography } from 'antd';
import { isEmpty } from 'lodash';
import { get, isEmpty } from 'lodash';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as VersionIcon } from '../../../assets/svg/ic-version.svg';
@ -89,13 +89,20 @@ function DataAssetsVersionHeader({
() => getDataAssetsVersionHeaderInfo(entityType, currentVersionData),
[entityType, currentVersionData]
);
const logo = useMemo(
() =>
serviceUtilClassBase.getServiceTypeLogo(
const icon = useMemo(() => {
const serviceType = get(currentVersionData, 'serviceType', '');
return serviceType ? (
<img
alt="service-icon"
className="h-9"
src={serviceUtilClassBase.getServiceTypeLogo(
currentVersionData as SearchSourceAlias
),
[currentVersionData]
);
)}
/>
) : null;
}, [currentVersionData]);
return (
<Row className="p-x-lg" gutter={[8, 12]} justify="space-between">
@ -108,11 +115,7 @@ function DataAssetsVersionHeader({
<EntityHeaderTitle
deleted={deleted}
displayName={displayName}
icon={
'serviceType' in currentVersionData && (
<img className="h-9" src={logo} />
)
}
icon={icon}
name={currentVersionData?.name}
serviceName={serviceName ?? ''}
/>

View File

@ -13,7 +13,7 @@
import { CloseOutlined } from '@ant-design/icons';
import { Col, Drawer, Row } from 'antd';
import { cloneDeep } from 'lodash';
import { cloneDeep, get } from 'lodash';
import { EntityDetailUnion } from 'Models';
import React, { useEffect, useMemo, useState } from 'react';
import { EntityType } from '../../../enums/entity.enum';
@ -29,7 +29,6 @@ import { StoredProcedure } from '../../../generated/entity/data/storedProcedure'
import { Table } from '../../../generated/entity/data/table';
import { Topic } from '../../../generated/entity/data/topic';
import { TagLabel } from '../../../generated/type/tagLabel';
import { SearchSourceAlias } from '../../../interface/search.interface';
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
import {
DRAWER_NAVIGATION_OPTIONS,
@ -72,18 +71,16 @@ const EntityInfoDrawer = ({
[selectedNode]
);
const icon = useMemo(
() =>
'serviceType' in selectedNode ? (
const icon = useMemo(() => {
const serviceType = get(selectedNode, 'serviceType', '');
return serviceType ? (
<img
className="h-9"
src={serviceUtilClassBase.getServiceTypeLogo(
selectedNode as SearchSourceAlias
)}
src={serviceUtilClassBase.getServiceTypeLogo(selectedNode)}
/>
) : null,
[selectedNode]
);
) : null;
}, [selectedNode]);
const tags = useMemo(
() =>

View File

@ -13,7 +13,7 @@
import { Button, Select } from 'antd';
import { AxiosError } from 'axios';
import { capitalize, debounce } from 'lodash';
import { capitalize, debounce, get } from 'lodash';
import React, {
FC,
HTMLAttributes,
@ -125,9 +125,7 @@ const NodeSuggestions: FC<EntitySuggestionProps> = ({
}}>
<div className="d-flex items-center w-full overflow-hidden">
<img
alt={
'serviceType' in entity ? entity.serviceType : entity.name
}
alt={get(entity, 'serviceType', '') || entity.name}
className="m-r-xs"
height="16px"
src={serviceUtilClassBase.getServiceTypeLogo(entity)}

View File

@ -75,7 +75,6 @@ import {
} from '../../generated/type/entityLineage';
import { useFqn } from '../../hooks/useFqn';
import { getLineageDataByFQN, updateLineageEdge } from '../../rest/lineageAPI';
import { isDeleted } from '../../utils/CommonUtils';
import {
addLineageHandler,
centerNodePosition,
@ -650,12 +649,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
} else {
setSelectedEdge(undefined);
setActiveNode(node);
const sourceTypeNode = node.data.node as SourceType;
setSelectedNode({
...sourceTypeNode,
// we are getting deleted as a string instead of boolean from API so need to handle it like this
deleted: isDeleted(sourceTypeNode.deleted),
});
setSelectedNode(node.data.node as SourceType);
setIsDrawerOpen(true);
handleLineageTracing(node);
}

View File

@ -90,7 +90,7 @@ import { ColumnLineage, LineageDetails } from '../generated/type/entityLineage';
import { EntityReference } from '../generated/type/entityReference';
import { TagSource } from '../generated/type/tagLabel';
import { addLineage, deleteLineageEdge } from '../rest/miscAPI';
import { getPartialNameFromTableFQN } from './CommonUtils';
import { getPartialNameFromTableFQN, isDeleted } from './CommonUtils';
import { getEntityName, getEntityReferenceFromEntity } from './EntityUtils';
import Fqn from './Fqn';
import { jsonToCSV } from './StringsUtils';
@ -728,6 +728,9 @@ export const createNodes = (
? node.type
: getNodeType(edgesData, node.id);
// we are getting deleted as a string instead of boolean from API so need to handle it like this
node.deleted = isDeleted(node.deleted);
return {
id: `${node.id}`,
sourcePosition: Position.Right,