Skip to content

Commit 872169a

Browse files
authored
Merge branch 'main' into fix-audit
2 parents a6d403d + 12c3d0c commit 872169a

2 files changed

Lines changed: 330 additions & 14 deletions

File tree

openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Entity.spec.ts

Lines changed: 311 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ import {
6363
waitForAllLoadersToDisappear,
6464
} from '../../utils/entity';
6565
import { visitServiceDetailsPage } from '../../utils/service';
66+
import { getCurrentMillis } from '../../utils/dateTime';
67+
import { clickDataQualityStatCard } from '../../utils/entityPanel';
6668
import { PLAYWRIGHT_SAMPLE_DATA_TAG_OBJ } from '../../constant/config';
69+
import { Column, Table } from '../../../src/generated/entity/data/table';
6770

6871
const entities = {
6972
'Api Endpoint': ApiEndpointClass,
@@ -1205,11 +1208,11 @@ Object.entries(entities).forEach(([key, EntityClass]) => {
12051208
await test.step(
12061209
'Verify array column with nested children renders correctly',
12071210
async () => {
1208-
const tableResponse = entity.entityResponseData as any;
1211+
const tableResponse = entity.entityResponseData as Table;
12091212
const columns = tableResponse?.columns || [];
12101213

12111214
const nestedParent = columns.find(
1212-
(col: any) => col.name === (entity as TableClass).columnsName[2]
1215+
(col: Column) => col.name === (entity as TableClass).columnsName[2]
12131216
);
12141217

12151218
if (!nestedParent) {
@@ -1223,7 +1226,7 @@ Object.entries(entities).forEach(([key, EntityClass]) => {
12231226
const nestedParentFQN = nestedParent.fullyQualifiedName;
12241227

12251228
const arrayColumn = nestedParent.children?.find(
1226-
(col: any) => col.name === (entity as TableClass).columnsName[4]
1229+
(col: Column) => col.name === (entity as TableClass).columnsName[4]
12271230
);
12281231

12291232
if (!arrayColumn) {
@@ -1706,6 +1709,311 @@ Object.entries(entities).forEach(([key, EntityClass]) => {
17061709
);
17071710
});
17081711
}
1712+
1713+
if (entity.type === 'Table') {
1714+
test(
1715+
'Column detail panel - Data Quality tab shows test cases',
1716+
async ({ page }) => {
1717+
test.slow();
1718+
1719+
const { apiContext, afterAction } = await getApiContext(page);
1720+
const tableEntity = entity as TableClass;
1721+
const tableFQN =
1722+
entity.entityResponseData?.['fullyQualifiedName'];
1723+
const columnName = tableEntity.columnsName[0];
1724+
let testCase1Name = '';
1725+
let testCase2Name = '';
1726+
1727+
try {
1728+
await tableEntity.createTestSuiteAndPipelines(apiContext);
1729+
1730+
const testCase1 = await tableEntity.createTestCase(apiContext, {
1731+
name: `pw_col_dq_success_${uuid()}`,
1732+
entityLink: `<#E::table::${tableFQN}::columns::${columnName}>`,
1733+
testDefinition: 'columnValueLengthsToBeBetween',
1734+
parameterValues: [
1735+
{ name: 'minLength', value: 1 },
1736+
{ name: 'maxLength', value: 10 },
1737+
],
1738+
});
1739+
1740+
const testCase2 = await tableEntity.createTestCase(apiContext, {
1741+
name: `pw_col_dq_failed_${uuid()}`,
1742+
entityLink: `<#E::table::${tableFQN}::columns::${columnName}>`,
1743+
testDefinition: 'columnValueLengthsToBeBetween',
1744+
parameterValues: [
1745+
{ name: 'minLength', value: 100 },
1746+
{ name: 'maxLength', value: 200 },
1747+
],
1748+
});
1749+
1750+
testCase1Name = testCase1.name;
1751+
testCase2Name = testCase2.name;
1752+
1753+
await tableEntity.addTestCaseResult(
1754+
apiContext,
1755+
testCase1.fullyQualifiedName,
1756+
{ testCaseStatus: 'Success', timestamp: getCurrentMillis() }
1757+
);
1758+
1759+
await tableEntity.addTestCaseResult(
1760+
apiContext,
1761+
testCase2.fullyQualifiedName,
1762+
{
1763+
testCaseStatus: 'Failed',
1764+
result: 'Column value length exceeded maximum',
1765+
timestamp: getCurrentMillis(),
1766+
}
1767+
);
1768+
1769+
await test.step(
1770+
'Open column detail panel and navigate to DQ tab',
1771+
async () => {
1772+
await redirectToHomePage(page);
1773+
await entity.visitEntityPage(page);
1774+
1775+
await page.getByTestId(entity.childrenTabId ?? '').click();
1776+
await waitForAllLoadersToDisappear(page);
1777+
1778+
await openColumnDetailPanel({
1779+
page,
1780+
rowSelector,
1781+
columnId: entity.childrenSelectorId ?? '',
1782+
columnNameTestId: 'column-name',
1783+
entityType: entity.type as EntityType,
1784+
});
1785+
1786+
const dqTabResponsePromise = page.waitForResponse(
1787+
(response) =>
1788+
response
1789+
.url()
1790+
.includes('/api/v1/dataQuality/testCases')
1791+
);
1792+
await page.getByTestId('data-quality-tab').click();
1793+
const dqTabResponse = await dqTabResponsePromise;
1794+
expect(dqTabResponse.status()).toBe(200);
1795+
await waitForAllLoadersToDisappear(page);
1796+
}
1797+
);
1798+
1799+
await test.step(
1800+
'Verify stat cards and filter by failed',
1801+
async () => {
1802+
const panelContainer = page.locator('.column-detail-panel');
1803+
const dqContent = panelContainer.locator(
1804+
'.data-quality-tab-container'
1805+
);
1806+
1807+
await expect(dqContent).toBeVisible();
1808+
1809+
const successStat = panelContainer.locator(
1810+
'[data-testid="data-quality-stat-card-success"]'
1811+
);
1812+
const failedStat = panelContainer.locator(
1813+
'[data-testid="data-quality-stat-card-failed"]'
1814+
);
1815+
1816+
await expect(successStat).toBeVisible();
1817+
await expect(failedStat).toBeVisible();
1818+
1819+
await expect(successStat).toHaveText('1Passed');
1820+
await expect(failedStat).toHaveText('1Failed');
1821+
1822+
await clickDataQualityStatCard(page, 'failed');
1823+
1824+
const testCaseCardsSection = dqContent.locator(
1825+
'.test-case-cards-section'
1826+
);
1827+
1828+
await expect(testCaseCardsSection).toBeVisible();
1829+
1830+
const failedCards =
1831+
testCaseCardsSection.locator('.test-case-card');
1832+
1833+
await expect(failedCards).toHaveCount(1);
1834+
1835+
const failedCard = failedCards.first();
1836+
1837+
await expect(
1838+
failedCard.locator('.test-case-name')
1839+
).toContainText(testCase2Name);
1840+
1841+
}
1842+
);
1843+
1844+
await test.step(
1845+
'Filter by success and verify test case card',
1846+
async () => {
1847+
const panelContainer = page.locator('.column-detail-panel');
1848+
const dqContent = panelContainer.locator(
1849+
'.data-quality-tab-container'
1850+
);
1851+
await clickDataQualityStatCard(page, 'success');
1852+
1853+
const testCaseCardsSection = dqContent.locator(
1854+
'.test-case-cards-section'
1855+
);
1856+
const successCards =
1857+
testCaseCardsSection.locator('.test-case-card');
1858+
1859+
await expect(successCards).toHaveCount(1);
1860+
await expect(
1861+
successCards.first().locator('.test-case-name')
1862+
).toContainText(testCase1Name);
1863+
1864+
await closeColumnDetailPanel(page);
1865+
}
1866+
);
1867+
} finally {
1868+
await afterAction();
1869+
}
1870+
}
1871+
);
1872+
1873+
test(
1874+
'Column detail panel - Data Quality Incidents tab',
1875+
async ({ page }) => {
1876+
test.slow();
1877+
1878+
const { apiContext, afterAction } = await getApiContext(page);
1879+
const tableEntity = entity as TableClass;
1880+
const tableFQN =
1881+
entity.entityResponseData?.['fullyQualifiedName'];
1882+
const columnName = tableEntity.columnsName[0];
1883+
1884+
try {
1885+
await tableEntity.createTestSuiteAndPipelines(apiContext);
1886+
1887+
const testCase = await tableEntity.createTestCase(apiContext, {
1888+
name: `pw_col_incident_${uuid()}`,
1889+
entityLink: `<#E::table::${tableFQN}::columns::${columnName}>`,
1890+
testDefinition: 'columnValueLengthsToBeBetween',
1891+
parameterValues: [
1892+
{ name: 'minLength', value: 100 },
1893+
{ name: 'maxLength', value: 200 },
1894+
],
1895+
});
1896+
1897+
await tableEntity.addTestCaseResult(
1898+
apiContext,
1899+
testCase.fullyQualifiedName,
1900+
{
1901+
testCaseStatus: 'Failed',
1902+
result: 'Column value length exceeded maximum',
1903+
timestamp: getCurrentMillis(),
1904+
}
1905+
);
1906+
1907+
await test.step(
1908+
'Open column detail panel and navigate to Incidents tab',
1909+
async () => {
1910+
await redirectToHomePage(page);
1911+
await entity.visitEntityPage(page);
1912+
1913+
await page.getByTestId(entity.childrenTabId ?? '').click();
1914+
await waitForAllLoadersToDisappear(page);
1915+
1916+
await openColumnDetailPanel({
1917+
page,
1918+
rowSelector,
1919+
columnId: entity.childrenSelectorId ?? '',
1920+
columnNameTestId: 'column-name',
1921+
entityType: entity.type as EntityType,
1922+
});
1923+
1924+
const dqTabResponsePromise = page.waitForResponse(
1925+
(response) =>
1926+
response
1927+
.url()
1928+
.includes('/api/v1/dataQuality/testCases')
1929+
);
1930+
await page.getByTestId('data-quality-tab').click();
1931+
const dqTabResponse = await dqTabResponsePromise;
1932+
expect(dqTabResponse.status()).toBe(200);
1933+
1934+
await waitForAllLoadersToDisappear(page);
1935+
1936+
const dqContent = page
1937+
.locator('.column-detail-panel')
1938+
.locator('.data-quality-tab-container');
1939+
1940+
await expect(dqContent).toBeVisible();
1941+
1942+
1943+
await dqContent
1944+
.locator('.data-quality-tabs')
1945+
.getByRole('tab', { name: /incidents/i })
1946+
.click();
1947+
await waitForAllLoadersToDisappear(page);
1948+
}
1949+
);
1950+
1951+
await test.step(
1952+
'Verify incidents stats container and cards',
1953+
async () => {
1954+
const panelContainer = page.locator('.column-detail-panel');
1955+
const dqContent = panelContainer.locator(
1956+
'.data-quality-tab-container'
1957+
);
1958+
const incidentsTabContent = dqContent.locator(
1959+
'.incidents-tab-content'
1960+
);
1961+
1962+
await expect(incidentsTabContent).toBeVisible();
1963+
1964+
const incidentStatsContainer = incidentsTabContent.locator(
1965+
'.incidents-stats-container'
1966+
);
1967+
1968+
await expect(incidentStatsContainer).toBeVisible();
1969+
1970+
await expect(
1971+
incidentStatsContainer.locator(
1972+
'.incident-stat-card.new-card'
1973+
)
1974+
).toBeVisible();
1975+
await expect(
1976+
incidentStatsContainer.locator(
1977+
'.incident-stat-card.ack-card'
1978+
)
1979+
).toBeVisible();
1980+
await expect(
1981+
incidentStatsContainer.locator(
1982+
'.incident-stat-card.assigned-card'
1983+
)
1984+
).toBeVisible();
1985+
await expect(
1986+
incidentStatsContainer.locator('.resolved-section')
1987+
).toBeVisible();
1988+
1989+
const incidentCardsSection = incidentsTabContent.locator(
1990+
'.incident-cards-section'
1991+
);
1992+
1993+
await expect(incidentCardsSection).toBeVisible();
1994+
1995+
const incidentCards =
1996+
incidentCardsSection.locator('.test-case-card');
1997+
const cardCount = await incidentCards.count();
1998+
1999+
if (cardCount > 0) {
2000+
const assigneeSection = incidentCards
2001+
.first()
2002+
.locator('.test-case-detail-item')
2003+
.filter({ hasText: /assignee/i });
2004+
2005+
await expect(assigneeSection).toBeVisible();
2006+
}
2007+
2008+
await closeColumnDetailPanel(page);
2009+
}
2010+
);
2011+
} finally {
2012+
await afterAction();
2013+
}
2014+
}
2015+
);
2016+
}
17092017
}
17102018

17112019
/**

0 commit comments

Comments
 (0)