Skip to content

Commit 3148527

Browse files
karanh37mohitjeswani01
authored andcommitted
chore(ui): add playwright for count comparison (open-metadata#27817)
* add playwright * Update ExploreAggregationCountsMatching.spec.ts * fix test * Update ExploreAggregationCountsMatching.spec.ts
1 parent 2594979 commit 3148527

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright 2025 Collate.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
14+
import { expect, Page, test } from '@playwright/test';
15+
import { redirectToHomePage } from '../../utils/common';
16+
17+
// Maps entityType keys from the API aggregation to the explore left-panel tab testid labels.
18+
// The testid format is `${lowerCase(tabDetail.label)}-tab` (see ExploreUtils.tsx generateTabItems).
19+
const ENTITY_TYPE_TO_TAB_TESTID: Record<string, string> = {
20+
table: 'tables-tab',
21+
database: 'databases-tab',
22+
databaseSchema: 'database schemas-tab',
23+
glossaryTerm: 'glossary terms-tab',
24+
dataProduct: 'data products-tab',
25+
dashboard: 'dashboards-tab',
26+
pipeline: 'pipelines-tab',
27+
topic: 'topics-tab',
28+
mlmodel: 'ml models-tab',
29+
container: 'containers-tab',
30+
searchIndex: 'search indexes-tab',
31+
chart: 'charts-tab',
32+
storedProcedure: 'stored procedures-tab',
33+
tag: 'tags-tab',
34+
metric: 'metrics-tab',
35+
};
36+
37+
const SEARCH_URL_FRAGMENT = '/api/v1/search/query';
38+
39+
async function runSearchValidation(page: Page): Promise<void> {
40+
const apiCountResPromise = page.waitForResponse(
41+
'api/v1/search/query?q=customers&index=dataAsset&*'
42+
);
43+
44+
// Capture the initial table tab search response which fires automatically on load
45+
const initialTabSearchResPromise = page.waitForResponse(
46+
(response) =>
47+
response.url().includes(SEARCH_URL_FRAGMENT) &&
48+
response.url().includes('size=15') &&
49+
response.url().includes('from=0') &&
50+
response.request().method() === 'GET'
51+
);
52+
53+
await page.getByTestId('searchBox').fill('customers');
54+
await page.getByTestId('searchBox').press('Enter');
55+
56+
const [apiCountRes, initialTabSearchRes] = await Promise.all([
57+
apiCountResPromise,
58+
initialTabSearchResPromise,
59+
]);
60+
const countResponseBody = await apiCountRes.json();
61+
const initialTabSearchBody = await initialTabSearchRes.json();
62+
63+
// Wait for the explore left panel to appear after search
64+
await page.getByTestId('explore-left-panel').waitFor({ state: 'visible' });
65+
66+
const aggregations = countResponseBody?.aggregations ?? {};
67+
const entityTypeBuckets: Array<{ key: string; doc_count: number }> =
68+
(aggregations['entityType'] ?? aggregations['sterms#entityType'])
69+
?.buckets ?? [];
70+
71+
await test.step('Verify left panel counts match API aggregation', async () => {
72+
for (const bucket of entityTypeBuckets) {
73+
const tabTestId = ENTITY_TYPE_TO_TAB_TESTID[bucket.key];
74+
75+
if (!tabTestId) {
76+
continue;
77+
}
78+
79+
const tabLocator = page.getByTestId(tabTestId);
80+
const isTabVisible = await tabLocator.isVisible();
81+
82+
if (!isTabVisible) {
83+
continue;
84+
}
85+
86+
const countLocator = tabLocator.getByTestId('filter-count');
87+
const countText = await countLocator.textContent();
88+
const displayedCount = parseInt(countText?.trim() ?? '0', 10);
89+
90+
expect(
91+
displayedCount,
92+
`Left panel count for "${bucket.key}" should match API count`
93+
).toBe(bucket.doc_count);
94+
}
95+
});
96+
97+
await test.step('Click each tab and verify search results match entity type', async () => {
98+
let isFirstTab = true;
99+
100+
for (const bucket of entityTypeBuckets) {
101+
const tabTestId = ENTITY_TYPE_TO_TAB_TESTID[bucket.key];
102+
103+
if (!tabTestId) {
104+
continue;
105+
}
106+
107+
const tabLocator = page.getByTestId(tabTestId);
108+
const isTabVisible = await tabLocator.isVisible();
109+
110+
if (!isTabVisible) {
111+
continue;
112+
}
113+
114+
let tabSearchBody: {
115+
hits: {
116+
total: { value: number };
117+
hits: Array<{ _source: { entityType: string } }>;
118+
};
119+
};
120+
121+
if (isFirstTab) {
122+
tabSearchBody = initialTabSearchBody;
123+
isFirstTab = false;
124+
} else {
125+
const tabSearchResPromise = page.waitForResponse(
126+
(response) =>
127+
response.url().includes(SEARCH_URL_FRAGMENT) &&
128+
response.url().includes('size=15') &&
129+
response.url().includes('from=0') &&
130+
response.request().method() === 'GET'
131+
);
132+
133+
await tabLocator.click();
134+
135+
const tabSearchRes = await tabSearchResPromise;
136+
tabSearchBody = await tabSearchRes.json();
137+
}
138+
139+
const totalHits: number = tabSearchBody?.hits?.total?.value ?? 0;
140+
141+
expect(
142+
totalHits,
143+
`Tab "${bucket.key}" search total hits should match the aggregation count`
144+
).toBe(bucket.doc_count);
145+
}
146+
});
147+
}
148+
149+
test.describe(
150+
'Explore Aggregation Counts Matching',
151+
{ tag: ['@Discovery'] },
152+
() => {
153+
test.use({
154+
storageState: 'playwright/.auth/admin.json',
155+
});
156+
157+
test.beforeEach(async ({ page }) => {
158+
await redirectToHomePage(page);
159+
});
160+
161+
test('should verify left panel counts and tab search results for normal search', async ({
162+
page,
163+
}) => {
164+
test.slow();
165+
166+
await runSearchValidation(page);
167+
});
168+
}
169+
);

0 commit comments

Comments
 (0)