Compare commits

...

1 Commits

Author SHA1 Message Date
Evan Rusackas
b6856e00c4 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 <noreply@anthropic.com>
2026-07-24 20:00:45 -07:00

View File

@@ -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,
});
},
);