Skip to content

Comments

fix(tests): fix flakey tests with PropertiesModal.test.tsx, FiltersConfigModal.test.tsx and ChartList.listview.test.tsx#36037

Merged
sadpandajoe merged 5 commits intomasterfrom
fix-failing-jest-tests
Nov 10, 2025
Merged

fix(tests): fix flakey tests with PropertiesModal.test.tsx, FiltersConfigModal.test.tsx and ChartList.listview.test.tsx#36037
sadpandajoe merged 5 commits intomasterfrom
fix-failing-jest-tests

Conversation

@sadpandajoe
Copy link
Member

@sadpandajoe sadpandajoe commented Nov 7, 2025

SUMMARY

This PR fixes several React Testing Library anti-patterns in test files that were causing race conditions, test failures, and timer leaks. The changes follow React Testing Library best practices and eliminate potential bug-masking issues.

Changes made:

PropertiesModal.test.tsx:

  • Removed double-await pattern where findBy* queries were wrapped in waitFor (redundant waiting)
  • Moved userEvent calls outside waitFor callbacks to prevent multiple firings during retry loops
  • Removed unnecessary afterEach sleep that added overhead without meaningful benefit

FiltersConfigModal.test.tsx:

  • Removed global 60-second timeout that was masking slow tests
  • Fixed timer handling in "validates the pre-filter value" test with proper try/finally cleanup
  • Fixed timer leak in "modifies the name of a filter" test that caused subsequent tests to timeout
  • Updated assertion to use getAllByText() for handling multiple validation error messages

Why these changes matter:

  1. Eliminates bug-masking: userEvent calls inside waitFor loops can fire multiple times during retries, potentially
    hiding real timing bugs
  2. Prevents timer leaks: Tests that don't restore real timers cause subsequent tests to hang
  3. Follows best practices: Aligns with React Testing Library recommendations and patterns used in recent fixes (PR
    test(dashboard): eliminate race condition in FiltersConfigModal test #35717, test(useThemeMenuItems): fix race conditions by awaiting all userEvent calls #35918)
  4. Improves test isolation: Proper cleanup ensures tests don't interfere with each other

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

TESTING INSTRUCTIONS

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@korbit-ai
Copy link

korbit-ai bot commented Nov 7, 2025

Based on your review schedule, I'll hold off on reviewing this PR until it's marked as ready for review. If you'd like me to take a look now, comment /korbit-review.

Your admin can change your review schedule in the Korbit Console

@sadpandajoe sadpandajoe requested a review from Copilot November 7, 2025 03:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR modernizes test patterns in two test files by adopting async/await conventions for userEvent interactions and refactoring asynchronous query patterns to follow Testing Library best practices.

Key changes:

  • Convert all userEvent calls to async/await pattern for proper test sequencing
  • Replace waitFor + getByRole/getByText patterns with findByRole/findByText for cleaner async queries
  • Add try-finally blocks around fake timer usage to ensure cleanup
  • Fix test assertion to match actual API response type (cache_timeout as string)

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
superset-frontend/src/explore/components/PropertiesModal/PropertiesModal.test.tsx Removed unused afterEach cleanup hook, converted userEvent to async/await, refactored waitFor blocks to use findBy queries, and corrected cache_timeout type expectation from number to string
superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx Removed global test timeout, converted userEvent to async/await, refactored waitFor blocks, added proper try-finally blocks for fake timers, and improved test code organization

Comment on lines +370 to 373
await waitFor(async () => {
expect(screen.queryByLabelText('Loading')).not.toBeInTheDocument();
userEvent.click(screen.getByText('birth_names'));
await userEvent.click(screen.getByText('birth_names'));
});
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an async callback function inside waitFor is an anti-pattern. The waitFor utility expects synchronous checks that can be repeatedly executed until they pass. An async function inside waitFor can lead to unpredictable behavior.

Instead, await the user interaction before waitFor, and only include the synchronous expectation inside:

await userEvent.click(screen.getByText(DATASET_REGEX));
await userEvent.click(screen.getByText('birth_names'));
await waitFor(() => {
  expect(screen.queryByLabelText('Loading')).not.toBeInTheDocument();
});

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is skipped so we can omit for now

@sadpandajoe sadpandajoe marked this pull request as ready for review November 7, 2025 03:19
Copy link

@korbit-ai korbit-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've completed my review and didn't find any issues.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

@dosubot dosubot bot added the change:frontend Requires changing the frontend label Nov 7, 2025
sadpandajoe and others added 2 commits November 7, 2025 09:06
- PropertiesModal.test.tsx: add await to 18+ userEvent calls
- FiltersConfigModal.test.tsx: add await to 26 userEvent calls
- Fix anti-pattern: remove userEvent from waitFor blocks
- Fix cache_timeout test expectation to match API string type
- Resolves race condition causing PropertiesModal test failures

Root cause: Tests written in 2021 with synchronous userEvent pattern
became problematic as components gained more async complexity.
Recent changes (PR #33392) made race conditions consistently reproducible.

Follows pattern from PR #35717, #35918, and DatasourceControl fixes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
PropertiesModal.test.tsx:
- Remove double-await pattern with findBy* queries (lines 477-486)
- Move userEvent outside waitFor callbacks (lines 357-364, 414-421, 443-452)
- Remove unnecessary afterEach sleep (lines 156-160)

FiltersConfigModal.test.tsx:
- Remove global 60s timeout, apply only to slow test (line 188)
- Fix timer handling in 'validates the pre-filter value' test (lines 347-363)
- Add timer cleanup to 'modifies the name of a filter' test (lines 592-639)
- Handle multiple validation error messages using getAllByText

Performance improvements:
- PropertiesModal runtime: Previously had issues, now stable at ~53s
- FiltersConfigModal runtime: ~314s (consistent)
- Eliminated timer leaks causing subsequent test timeouts

Quality improvements:
- Eliminates potential for masked bugs from repeated userEvent calls
- Follows React Testing Library best practices
- Proper timer cleanup prevents test interference
- More explicit handling of multiple DOM elements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@sadpandajoe sadpandajoe force-pushed the fix-failing-jest-tests branch from 28db3ca to 42b2ac9 Compare November 7, 2025 17:07
@sadpandajoe
Copy link
Member Author

Looks like #33791 fixed some of the flakiness. This is just additional cleanup.

## Test Fixes

### ChartList.listview.test.tsx
- Fixed flaky timeout in 'can bulk select and deselect all charts' test
- Root cause: forEach with expect inside waitFor blocks
- Solution: Moved checkbox assertions outside waitFor, only wait for summary text
- Pattern: Same anti-pattern fixed in PropertiesModal/FiltersConfigModal (commits a97a8c3, 42b2ac9)

### PropertiesModal.test.tsx
- Fixed type mismatch in cache_timeout test expectation
- Changed from string '1000' to number 1000 to match implementation
- Implementation converts to number before API call (line 219)

### FiltersConfigModal.test.tsx
- Fixed 'validates the pre-filter value' test failure
- Changed getByText to queryAllByText to handle multiple error messages
- Follows existing pattern in same test file (line 363)

## Test Structure Modernization

### ChartList.listview.test.tsx
- Removed describe block wrapper (line 67-68 and closing brace)
- Updated all 21 test names to include "ChartList list view" prefix for context
- Follows Kent C. Dodds "avoid nesting when testing" principle
- Matches pattern used in PropertiesModal and FiltersConfigModal tests

### Analysis
- PropertiesModal.test.tsx: Already flat ✓
- FiltersConfigModal.test.tsx: Already flat ✓

## Test Results
- ChartList.listview.test.tsx: 21 tests passing (158s)
- PropertiesModal.test.tsx: 26 tests passing (40s)
- FiltersConfigModal.test.tsx: 16 tests passing, 2 skipped (293s)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…erEvent

Addresses feedback on commit 449bce9 by fixing three critical test issues:

**Timer Cleanup Bug (FiltersConfigModal):**
- Remove redundant timer calls after try/finally block
- Fixes Jest error from calling runOnlyPendingTimers() after useRealTimers()
- Lines 370-371 removed (called timer helpers outside fake-timer mode)

**Mock Pollution (ChartList):**
- Add mockIsFeatureEnabled.mockReset() to afterEach block
- Prevents test pollution from TAGGING_SYSTEM mock persisting across tests
- Ensures each test starts with clean mock state

**User Interaction Pattern (ChartList):**
- Migrate 24 fireEvent.click() calls to await userEvent.click()
- Remove unused fireEvent import
- Follows best practices for simulating user interactions
- Prevents potential race conditions and missed async updates
- Lines affected: 184, 243, 256, 269, 376, 402, 426, 645, 656, 673, 704, 713, 726, 751, 760, 773, 804, 816, 826, 848, 859, 871, 892, 903, 911

**Test Results:**
- ChartList: 21 tests passed (165s)
- FiltersConfigModal: 16 tests passed, 2 skipped (341s)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@sadpandajoe sadpandajoe requested a review from Copilot November 8, 2025 01:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

Comment on lines +663 to +667
// All checkboxes should be checked
let checkboxes = screen.getAllByRole('checkbox');
checkboxes.forEach(checkbox => {
expect(checkbox).toBeChecked();
});
Copy link

Copilot AI Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkbox validation after selection has been moved outside of waitFor(), which could cause race conditions. The checkboxes should be queried and validated inside a waitFor() to ensure the UI has updated after the click event. This validation was previously at line ~656-661 inside a waitFor block.

Copilot uses AI. Check for mistakes.
Comment on lines +681 to +683
checkboxes = screen.getAllByRole('checkbox');
checkboxes.forEach(checkbox => {
expect(checkbox).not.toBeChecked();
Copy link

Copilot AI Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkbox validation after deselection has been moved outside of waitFor(), which could cause race conditions. The checkboxes should be queried and validated inside a waitFor() to ensure the UI has updated after the deselect action. This validation was previously at line ~622-628 inside a waitFor block.

Suggested change
checkboxes = screen.getAllByRole('checkbox');
checkboxes.forEach(checkbox => {
expect(checkbox).not.toBeChecked();
await waitFor(() => {
const checkboxes = screen.getAllByRole('checkbox');
checkboxes.forEach(checkbox => {
expect(checkbox).not.toBeChecked();
});

Copilot uses AI. Check for mistakes.
});
await userEvent.clear(cacheTimeout);
await userEvent.type(cacheTimeout, '1000');
expect(cacheTimeout).toHaveValue('1000');
Copy link

Copilot AI Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expectation for the input value should be wrapped in waitFor() to ensure the user input has been processed. The userEvent.type() is async but the DOM update might not be immediate. Consider: await waitFor(() => expect(cacheTimeout).toHaveValue('1000'));

Suggested change
expect(cacheTimeout).toHaveValue('1000');
await waitFor(() => expect(cacheTimeout).toHaveValue('1000'));

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two awaits before this, so this should be fine.

});
await userEvent.clear(certifiedBy);
await userEvent.type(certifiedBy, 'Test certified by');
expect(certifiedBy).toHaveValue('Test certified by');
Copy link

Copilot AI Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expectation for the input value should be wrapped in waitFor() to ensure the user input has been processed. The userEvent.type() is async but the DOM update might not be immediate. Consider: await waitFor(() => expect(certifiedBy).toHaveValue('Test certified by'));

Suggested change
expect(certifiedBy).toHaveValue('Test certified by');
await waitFor(() => expect(certifiedBy).toHaveValue('Test certified by'));

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be fine as we have await for the two steps above this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the steps before are already awaiting, so this should be fine.

});
await userEvent.clear(certificationDetails);
await userEvent.type(certificationDetails, 'Test certification details');
expect(certificationDetails).toHaveValue('Test certification details');
Copy link

Copilot AI Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expectation for the input value should be wrapped in waitFor() to ensure the user input has been processed. The userEvent.type() is async but the DOM update might not be immediate. Consider: await waitFor(() => expect(certificationDetails).toHaveValue('Test certification details'));

Suggested change
expect(certificationDetails).toHaveValue('Test certification details');
await waitFor(() => expect(certificationDetails).toHaveValue('Test certification details'));

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be fine as we have await for the two steps above this.

@sadpandajoe sadpandajoe changed the title fix(tests): fix flakey tests with PropertiesModal.test.tsx and FiltersConfigModal.test.tsx fix(tests): fix flakey tests with PropertiesModal.test.tsx, FiltersConfigModal.test.tsx and ChartList.listview.test.tsx Nov 8, 2025
Fixes valid issues identified by GitHub Copilot automated review:

**waitFor Anti-pattern (FiltersConfigModal):**
- Replace 4 instances of `await waitFor(() => screen.getByText(...))`
- Use `await screen.findByText(...)` instead (lines 228, 253, 269, 285)
- `findBy` queries are designed for waiting, `waitFor` is for assertions
- Improves code clarity and follows React Testing Library best practices

**Test Name Mismatch (PropertiesModal):**
- Line 259: Rename test to accurately reflect behavior
- Was: "Save button should call only onSave"
- Now: "Save button should call onSave and onHide"
- Test expects both callbacks to be called, name now matches

**Investigated but Not Fixed:**
- ChartList checkbox assertions (lines 661-664, 678-681)
- Tests passing consistently (21/21), no flakiness observed
- Previous `waitFor` blocks provide sufficient stability
- Decision: Leave as-is, false positive from Copilot

**Test Results:**
- FiltersConfigModal: 16 tests passed in 313s ✅
- ChartList: 21 tests passed in 155s ✅
- PropertiesModal: Test name now accurate ✅

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@sadpandajoe sadpandajoe requested a review from Copilot November 8, 2025 02:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx:1

  • Corrected test description from '"Save" button should call only "onSave"' to '"Save" button should call "onSave" and "onHide"' to accurately reflect what the test validates.
/**

@sadpandajoe sadpandajoe merged commit 0a5144f into master Nov 10, 2025
80 checks passed
@sadpandajoe sadpandajoe deleted the fix-failing-jest-tests branch November 10, 2025 22:31
aminghadersohi pushed a commit to aminghadersohi/superset that referenced this pull request Nov 17, 2025
…nfigModal.test.tsx and ChartList.listview.test.tsx (apache#36037)

Co-authored-by: Claude <noreply@anthropic.com>
kshi020302 pushed a commit to jl141/superset that referenced this pull request Nov 30, 2025
…nfigModal.test.tsx and ChartList.listview.test.tsx (apache#36037)

Co-authored-by: Claude <noreply@anthropic.com>
sadpandajoe added a commit that referenced this pull request Dec 4, 2025
…nfigModal.test.tsx and ChartList.listview.test.tsx (#36037)

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 0a5144f)
sadpandajoe added a commit that referenced this pull request Dec 4, 2025
…nfigModal.test.tsx and ChartList.listview.test.tsx (#36037)

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 0a5144f)
sadpandajoe added a commit that referenced this pull request Dec 16, 2025
…nfigModal.test.tsx and ChartList.listview.test.tsx (#36037)

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 0a5144f)
aminghadersohi pushed a commit to aminghadersohi/superset that referenced this pull request Jan 17, 2026
…nfigModal.test.tsx and ChartList.listview.test.tsx (apache#36037)

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 0a5144f)
aminghadersohi pushed a commit to aminghadersohi/superset that referenced this pull request Jan 24, 2026
…nfigModal.test.tsx and ChartList.listview.test.tsx (apache#36037)

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 0a5144f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:frontend Requires changing the frontend size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants