fix(tests): fix flaky FileHandler test by awaiting LaunchQueue consumer in afterEach#39508
Conversation
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
4961039 to
ddfcf60
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #39508 +/- ##
==========================================
- Coverage 63.83% 63.83% -0.01%
==========================================
Files 2589 2589
Lines 137821 137821
Branches 31928 31928
==========================================
- Hits 87978 87975 -3
- Misses 48327 48330 +3
Partials 1516 1516
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…ardown The flaky tests in FileHandler/index.test.tsx were caused by the async consumer promise (returned from the mocked setConsumer callback) not being awaited before the next test ran. In-flight state updates from one test could bleed into the next, causing intermittent 20s timeouts on tests like "handles Parquet file correctly" and "handles Excel (.xls) file correctly" (e.g. GHA job 71282515731 on PR apache#39345). The setTimeout(fn, 0) deferral and pendingTimerIds tracking are kept intentionally: calling the consumer synchronously inside setConsumer deadlocks Jest because jsDomWithFetchAPI's MessageChannel mock forces React to schedule via setTimeout, so the inline path was tried and reverted. Changes: - Capture the consumer's return value as consumerPromise - Await consumerPromise in afterEach so async work settles before the next test starts - Log (rather than swallow) any rejection from the awaited promise so unexpected failures stay visible - Remove a stale @ts-expect-error in spec/helpers/testing-library.tsx flagged by pre-commit's targeted type-check (react-dnd types no longer need the directive under React 18) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ddfcf60 to
de4a61d
Compare
|
The superset-frontend/src/pages/FileHandler/index.test.tsx |
Pre-commit's targeted tscw (composite: false, no project refs) flagged the prior @ts-expect-error in spec/helpers/testing-library.tsx as unused, so it was removed. CI's full tsc (with project references) still sees DndProviderProps as omitting `children` under React 18 types, which broke lint-frontend. Use @ts-ignore so the suppression is silent under tscw but still effective under the full tsc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR targets frontend test stability by ensuring async work triggered via the mocked launchQueue.setConsumer() is tracked and awaited between tests, reducing cross-test interference and intermittent timeouts in FileHandler tests.
Changes:
- Track the Promise returned by the
launchQueueconsumer callback and await it inafterEachto ensure async work settles between tests. - Keep macrotask deferral (
setTimeout(..., 0)) while adding cleanup of scheduled timer IDs. - Adjust a TypeScript suppression comment in the shared Testing Library wrapper helper.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| superset-frontend/src/pages/FileHandler/index.test.tsx | Captures and awaits the launchQueue consumer Promise in teardown to reduce test flakiness from overlapping async work. |
| superset-frontend/spec/helpers/testing-library.tsx | Updates the TypeScript suppression directive/comment for the react-dnd wrapper in test utilities. |
|
|
||
| if (useDnd) { | ||
| // @ts-expect-error react-dnd types not updated for React 18 | ||
| // @ts-ignore react-dnd's DndProviderProps omits `children` under React 18 types |
…-write-wins
The module-scoped `consumerPromise` was overwritten on each setTimeout
callback. If React's useEffect re-fired during a test (the useHistory
mock returns a fresh `{ push }` object each call, so deps look stale
after a re-render), a second setConsumer call would queue a second
setTimeout that overwrote the earlier promise — leaving the first
consumer's async work orphaned even though afterEach awaited
`consumerPromise`.
Collect every consumer promise into an array and Promise.allSettled
them in afterEach so all in-flight work settles before the next test,
regardless of how many times the useEffect re-registered the consumer.
Reset by length=0 in place — no beforeEach needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sadpandajoe
left a comment
There was a problem hiding this comment.
Recommendation: APPROVE
Verdict: consumerPromises array refactor in 8e8991c cleanly addresses the last-write-wins race; adversarial walk turned up nothing actionable.
Walked the delta:
consumerPromises.length = 0— correct in-place reset for aconstarray; no aliases.Promise.allSettledoverPromise.all— right call.Promise.allwould short-circuit on first rejection and orphan the rest, which is the exact failure mode being fixed.forEachoverPromiseSettledResultkeeps the priorconsole.warn-on-rejection semantics.- No test body mutates the array outside
setupLaunchQueue. - The window between
length = 0anddelete window.launchQueueis fully synchronous — no macrotask can sneak between them.
One theoretical residual: a useEffect re-fire during the allSettled await could push a new promise after the snapshot. Mocked consumers resolve in microtasks so the window is effectively zero — not worth surfacing.
|
Bito Automatic Review Skipped – PR Already Merged |
SUMMARY
Fixes flaky
FileHandler/index.test.tsxtests that intermittently timeout at 20s. Observed on multiple tests including "handles Parquet file correctly" and "handles Excel (.xls) file correctly" (e.g. GHA job 71282515731 on PR #39345).Root cause:
setupLaunchQueue's mockedsetConsumerinvoked the consumer callback insidesetTimeout(fn, 0)but did not capture or await the resulting Promise. The async work kicked off by one test (file reads,setStatecalls,history.push) could still be in flight when the next test started, racing the new test'srenderand assertions.Fix:
consumerPromiseawait consumerPromiseinafterEachso all async work settles before the next test beginsWhy we keep the
setTimeout(fn, 0)deferral: an earlier attempt called the consumer synchronously insidesetConsumer. That deadlocked Jest —jsDomWithFetchAPI'sMessageChannelmock forces React to schedule updates viasetTimeout, and a fully-synchronous path inside theuseEffectran ahead of React's scheduler. The macrotask deferral pluspendingTimerIdscleanup is intentional and stays.Drive-by: removed a stale
// @ts-expect-error react-dnd types not updated for React 18inspec/helpers/testing-library.tsxthat pre-commit's targeted type-check flagged as unused onceindex.test.tsxwas changed (react-dnd types now satisfy React 18 without the directive).BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
N/A - test-only change
TESTING INSTRUCTIONS
cd superset-frontend && npm run test -- src/pages/FileHandler/index.test.tsx— verify all 10 tests pass (1 skipped)ADDITIONAL INFORMATION