lizhimins opened a new pull request, #4200: URL: https://github.com/apache/rocketmq-dashboard/pull/4200
Two frontend tests were failing intermittently with `Unable to perform pointer interaction as the element has pointer-events: none` — on an `INPUT` (a row selection checkbox) in `AlertsPage.test.tsx` and on an `LI` (a pagination item) in `ConsumerPage.test.tsx`. Both are the same race, and it is in the tests rather than in the pages. ## Root cause antd's `Table` keeps the previously rendered rows on screen while `loading` is true, and wraps the body — pagination included — in a `Spin`. `Spin` tracks its own `spinning` state (`antd/es/spin/index.js:52`) and only syncs it from the `spinning` prop inside an effect (`:54-66`), while the `.ant-spin-blur` class is keyed on that internal state (`:80`). So when the Table's `loading` prop flips to false, the blur class survives **one further commit**. `.ant-spin-blur` sets `pointer-events: none` (`antd/es/spin/style/index.js:133-137`), covering every row control plus the pagination items that antd renders inside the same spin container (`antd/es/table/InternalTable.js:396`). `findByText` on a row name resolves during that window because the rows are already in the DOM, and the click that follows is rejected by userEvent's pointer-events check. Instrumenting the failing runs confirms the mechanism and rules out the alternatives: | | failing run | passing run | | :-- | :-- | :-- | | `.ant-spin-blur` present | 1 | 0 | | `.ant-spin-spinning` present | 1 | 0 | | checkbox `disabled` | **false** | false | | computed `pointer-events` (input / row) | `none` / `none` | `auto` / `auto` | | `bulkToggleAlertRules` calls | 0 | 0 | | tables / text matches in DOM | 1 / 1 | 1 / 1 | The checkbox is *not* disabled (`getCheckboxProps` only disables while `isActionRunning`, and no action had run), there is no stale DOM from a previous test, and the row text matches exactly once — so the only thing blocking the click is the spin blur. ## Why the fix is test-side Batching `setLoading(false)` into the same `.then` as the row updates in `pages/ops/alerts.tsx` was tried first and **reverted**. It removes our own intermediate commit but not Spin's, so it lowered the failure rate without eliminating it (1 failure in 10 runs, versus 1 in 6 before). And since both microtasks drain before the browser paints, that intermediate state is invisible to users — it is only observable from tests. There was nothing to fix on the page side. ## What changed Each of the two test files gains a small wait for the target to accept pointer events, applied **only** where a test clicks a table-internal control immediately after a `findBy*` with nothing polled in between: - `AlertsPage.test.tsx` — `expectRuleRowInteractive(ruleName)`, used at 3 sites (two 编辑 button clicks and the row checkbox before 批量启用). - `ConsumerPage.test.tsx` — `findInteractivePageItem(selector)` at the two pagination clicks, and `expectInteractive(element)` for the row checkbox that follows a page change. Sites that already wait on something else — e.g. `await waitFor(() => expect(checkbox).toBeEnabled())` at `AlertsPage.test.tsx:247` and `:696` — are deliberately left untouched. That poll absorbs the extra commit, which is exactly why those tests never failed. The `ConsumerPage` pagination waits are preventive: that file produced one observed failure with the identical signature and contains the identical code pattern. No production code is changed. ## Validation - `npx tsc --noEmit -p tsconfig.app.json` — clean. - `npx eslint` on both files — clean, 0 errors. - `npm run build` — succeeds. - `AlertsPage.test.tsx` under 3-way concurrent load: **30/30 passed** (baseline before the fix: 1 failure in 6 sequential runs, and 1 in 12 on a second measurement). - `ConsumerPage.test.tsx` under 3-way concurrent load: **15/15 passed**. - Full frontend suite (`vitest run`, 120 files / 980 tests): **6 consecutive clean runs, 0 failures, 0 unhandled errors**. Before this change the suite produced one failure per run, alternating between these two files. Note that `tsc --noEmit -p tsconfig.json` is not a usable check in this repo — `tsconfig.json` is a solution file with `"files": []` and only project references, so it type-checks nothing and exits 0 regardless. Use `tsconfig.app.json`, or `npm run build`, whose script is `tsc -b && vite build`. ## Follow-up (not in this PR) The same latent pattern — `findBy*` on table content followed immediately by a click on a control inside the table — may exist in other test files. If more instances surface it is worth promoting one shared helper into `src/test/` rather than keeping a copy per file. Separately, 18 call sites across 12 frontend files set data in `.then()` and clear a loading flag in `.finally()`. Those are two microtasks, so they always commit an intermediate state where the data is present and the spinner is still on. Browsers drain both before painting, so this is not user-visible and is not touched here; it is noted only because it is what makes the intermediate state reachable from a test in the first place. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
