This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch test/drill-to-detail-modal-height in repository https://gitbox.apache.org/repos/asf/superset.git
commit b6856e00c444c77e7985fdf24c7aab162538fa55 Author: Evan Rusackas <[email protected]> AuthorDate: Fri Jul 24 20:00:45 2026 -0700 test(playwright): pin drill-to-detail modal table height The drill pane hands a resize-detector-measured height to a virtualized table, wired through CSS selectors on antd's internal modal classes that no unit test exercises. When the v6 upgrade renamed .ant-modal-content to .ant-modal-container the chain silently broke and the table collapsed to an empty body after a brief flash of rows. Only a real browser sees layout, so pin it end-to-end: the body must hold a real height across two consecutive reads (a single read could pass during the flash) and contain actual sample data. Verified red against a pre-fix bundle (body height settles at 1px) and the failing assertion names the collapse directly. Co-Authored-By: Claude Fable 5 <[email protected]> --- .../tests/dashboard/drill-to-detail.spec.ts | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/superset-frontend/playwright/tests/dashboard/drill-to-detail.spec.ts b/superset-frontend/playwright/tests/dashboard/drill-to-detail.spec.ts new file mode 100644 index 00000000000..08ffeca6ecd --- /dev/null +++ b/superset-frontend/playwright/tests/dashboard/drill-to-detail.spec.ts @@ -0,0 +1,130 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/** + * Regression coverage for the Drill to Detail modal's results table. + * + * The drill pane measures its available height with a resize detector and + * hands it to a virtualized table, so the rows only render if the modal's + * internal height chain (resizable wrapper -> modal container -> modal body -> + * flex pane) actually resolves to a real height. That chain is wired together + * with CSS selectors targeting Ant Design's internal modal classes, which + * TypeScript cannot see and unit tests do not exercise: when the antd v6 + * upgrade renamed `.ant-modal-content` to `.ant-modal-container`, the chain + * silently broke, the pane measured ~0, and the table briefly flashed its rows + * before collapsing to an empty body with only the header and pagination + * visible. + * + * Only a real browser sees layout, so this is pinned here rather than in the + * DOM-contract unit suite. Because the failure mode is + * render-then-collapse, a single "rows are visible" read could pass during + * the initial flash — the assertion therefore requires the table body to hold + * a real height across two consecutive reads. + * + * CI green => the drill modal's table renders rows at a stable, non-collapsed + * height. + * CI red => the modal height chain broke again (or drill-to-detail failed to + * open/load at all). + */ +import { testWithAssets, expect } from '../../helpers/fixtures'; +import { TIMEOUT } from '../../utils/constants'; +import { DashboardPage } from '../../pages/DashboardPage'; +import { createDashboardWithCharts } from './dashboard-test-helpers'; + +const MIN_STABLE_BODY_HEIGHT = 100; + +testWithAssets( + 'drill to detail modal renders result rows at a stable height', + async ({ page, testAssets }) => { + testWithAssets.setTimeout(TIMEOUT.SLOW_TEST); + + const { dashboardId } = await createDashboardWithCharts( + page, + testAssets, + testWithAssets.info(), + { + datasetName: 'birth_names', + chartNamePrefix: 'drill_detail', + dashboardTitlePrefix: 'drill_detail_modal', + chartSpecs: [ + { + viz_type: 'pie', + params: { + groupby: ['gender'], + metric: 'count', + }, + }, + ], + }, + ); + + const dashboard = new DashboardPage(page); + await dashboard.gotoById(dashboardId); + await dashboard.waitForLoad(); + await dashboard.waitForChartsToLoad(); + + // Open the chart context menu. The ECharts canvas exposes no data-test + // hooks for its regions, so right-click the centre of the chart container; + // the exact-text match below then works whether the click landed on a + // slice (which adds "Drill to detail by" items) or on the chart background. + // The first right-click after load can be swallowed by a chart re-render + // closing the menu, so retry the click until the menu actually shows. + const chart = page.locator('[data-test="chart-container"]').first(); + await chart.scrollIntoViewIfNeeded(); + const contextMenu = page.locator('[data-test="chart-context-menu"]'); + await expect(async () => { + const box = await chart.boundingBox(); + if (!box) { + throw new Error('chart container has no bounding box'); + } + await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2, { + button: 'right', + }); + await expect(contextMenu).toBeVisible({ timeout: 2000 }); + }).toPass({ timeout: TIMEOUT.CHART_RENDER }); + await page + .getByRole('menuitem', { name: 'Drill to detail', exact: true }) + .click(); + + const modal = page.locator('.ant-modal:visible'); + await expect(modal).toBeVisible({ timeout: TIMEOUT.FORM_LOAD }); + + // Wait for the samples request to resolve into a rendered table: the row + // count pill and the virtualized body both come from the loaded page. + const tableBody = modal.locator('.virtual-grid'); + await expect(tableBody).toBeAttached({ timeout: TIMEOUT.CHART_RENDER }); + + // The regression collapses the body *after* first paint, so require the + // height to hold across two consecutive reads rather than sampling once + // (a single read could land inside the flash and pass a broken build). + await expect(async () => { + const first = await tableBody.boundingBox(); + expect(first?.height ?? 0).toBeGreaterThan(MIN_STABLE_BODY_HEIGHT); + await page.waitForTimeout(300); + const second = await tableBody.boundingBox(); + expect(second?.height ?? 0).toBeGreaterThan(MIN_STABLE_BODY_HEIGHT); + }).toPass({ timeout: TIMEOUT.CHART_RENDER }); + + // And the rows are real data, not just an expanded empty scroller: + // birth_names sample rows always carry a gender value. + await expect(modal.getByText(/^(boy|girl)$/).first()).toBeVisible({ + timeout: TIMEOUT.API_RESPONSE, + }); + }, +);
