Refactor EventListCard components to use the shared DropDownButton#7286
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughReplaced the react-bootstrap Dropdown with a shared DropDownButton in RecurrenceDropdown, removed its external isOpen/onToggle props, consolidated EventForm modal state to useModalState, and updated multiple tests and test IDs to reflect a toggle-based recurrence UI and i18n test wrapper. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User as User
participant DropDownButton as "RecurrenceDropdown\n(DropDownButton)"
participant EventForm as EventForm
participant CustomModal as "CustomRecurrenceModal"
User->>DropDownButton: click recurrence-toggle
DropDownButton->>User: show options (recurrence-item-*)
User->>DropDownButton: select option (option or "custom")
DropDownButton->>EventForm: onSelect(option)
alt option == "custom"
EventForm->>CustomModal: customRecurrenceModal.open()
CustomModal->>User: render custom recurrence modal
User->>CustomModal: submit / close
CustomModal->>EventForm: customRecurrenceModal.close()
else option != "custom"
EventForm->>EventForm: apply selected recurrence option
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning, 2 inconclusive)
✅ Passed checks (6 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Our Pull Request Approval ProcessThis PR will be reviewed according to our: Your PR may be automatically closed if:
Thanks for contributing! |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/shared-components/EventForm/EventForm.spec.tsx (4)
222-224:⚠️ Potential issue | 🟠 Major❌ BLOCKING — Use
vi.restoreAllMocks()and addcleanup()in afterEach.
vi.clearAllMocks()only clears call history but keeps mock implementations, causing mock leakage between tests in parallel shards. Also,cleanup()from@testing-library/reactis missing.Proposed fix
+import { render, screen, act, waitFor, cleanup } from '@testing-library/react'; -import { render, screen, act, waitFor } from '@testing-library/react';afterEach(() => { - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); });As per coding guidelines: "❌ BLOCKING - Use vi.restoreAllMocks() at file:line" and "❌ BLOCKING - Missing required cleanup at file:line".
76-76:⚠️ Potential issue | 🟠 Major🔴 NON-DETERMINISTIC TIMESTAMP at Line 76 —
dayjs()without arguments in mock.
dayjs().format('YYYY-MM-DD')captures current time, causing flakiness in sharded CI. Use a fixed UTC string instead.Proposed fix
- const today = dayjs().format('YYYY-MM-DD'); + const today = '2025-06-15';As per coding guidelines:
dayjs()without arguments is a forbidden pattern — "Replace with fixed UTC string".
198-199:⚠️ Potential issue | 🟠 Major🔴 NON-DETERMINISTIC TIMESTAMPS —
dayjs()without arguments for test data.Lines 198-199 use
dayjs().add(...)which captures current time. These should use fixed UTC dates for deterministic test behavior across CI shards.The same pattern recurs at lines 253, 261, 315, 357, 401, 410, 454-456, 486, 500, 609-610, 653-654, 701, 1072-1073, 1118-1119, 1164-1165, 1208-1209, 1247-1248, 1288-1289, 1489, 1513-1514, 1612-1613, 1654-1655, 1696-1697, 1738-1739, 1818-1819, 1905-1906, and the mock callbacks at lines 131-134, 157.
Example fix for top-level dates
-const futureStartDate = dayjs().add(30, 'day').startOf('day').toDate(); -const futureEndDate = dayjs().add(31, 'day').startOf('day').toDate(); +const futureStartDate = new Date('2027-06-15T00:00:00.000Z'); +const futureEndDate = new Date('2027-06-16T00:00:00.000Z');As per coding guidelines: "
dayjs()without arguments" and "new Date()without arguments" are forbidden patterns.
238-246:⚠️ Potential issue | 🟠 Major🔴 RACE CONDITION — Assertion outside
waitForafter asyncuser.click.
expect(handleSubmit).toHaveBeenCalledWith(...)at line 239 followsawait user.click(...)at line 238 but is not wrapped inwaitFor. This pattern recurs extensively throughout the file (e.g., lines 278, 338, 380, 427, 479, 534, 554, 574, 602, 641, 679, 711, 737, 763, 787, 935, 950, 964, 995, 1024, 1061, 1107, 1153, 1199, 1243, 1351, 1447, 1465, 1643, 1685, 1727, 1769, 1963, 1992).Example fix:
await user.click(screen.getByTestId('createEventBtn')); -expect(handleSubmit).toHaveBeenCalledWith( +await waitFor(() => expect(handleSubmit).toHaveBeenCalledWith( expect.objectContaining({ name: 'Test Event' }), -); +));As per coding guidelines: "❌ BLOCKING - Race condition — Assertion outside waitFor block after async operation".
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx (1)
30-36:⚠️ Potential issue | 🟠 Major❌ BLOCKING — Use
vi.restoreAllMocks()and addcleanup()in afterEach.Both
beforeEachandafterEachusevi.clearAllMocks(). TheafterEachmust usevi.restoreAllMocks()and includecleanup()to prevent mock leakage and DOM pollution across sharded tests.Proposed fix
+import { render, screen, cleanup } from '@testing-library/react'; -import { render, screen } from '@testing-library/react';afterEach(() => { - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); });As per coding guidelines: "❌ BLOCKING - Use vi.restoreAllMocks()" and "❌ BLOCKING - Missing required cleanup".
🤖 Fix all issues with AI agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`:
- Around line 82-87: The test has race conditions because it asserts on onSelect
immediately after await user.click(...)—wrap each post-click assertion (e.g.,
the expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ label:
'Daily' })) after user.click(screen.getByTestId('recurrence-item-1'))) inside a
waitFor callback and add waitFor to the test imports; apply the same change for
the other occurrences (the assertions after user.click at the locations using
recurrence-item-*, and the block around the multi-line expects at lines
analogous to 183-196) so all click-triggered assertions wait for the async
update before asserting.
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx (2)
34-36:⚠️ Potential issue | 🟠 Major❌ BLOCKING — Missing
cleanup()and usesvi.clearAllMocks()instead ofvi.restoreAllMocks().The
afterEachblock is missingcleanup()from@testing-library/reactand usesvi.clearAllMocks()which only clears call history but keeps mock implementations, risking mock leakage between tests in parallel shards.Proposed fix
+import { render, screen, waitFor, cleanup } from '@testing-library/react'; -import { render, screen, waitFor } from '@testing-library/react'; ... afterEach(() => { - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); });As per coding guidelines: "❌ BLOCKING - Missing required cleanup" and "❌ BLOCKING - Use vi.restoreAllMocks() instead of vi.clearAllMocks()".
30-32:⚠️ Potential issue | 🟠 Major
beforeEachshould also usevi.restoreAllMocks().Same reasoning —
vi.clearAllMocks()only resets call counts/arguments but leaves mock implementations intact.Proposed fix
beforeEach(() => { - vi.clearAllMocks(); + vi.restoreAllMocks(); });As per coding guidelines: "vi.clearAllMocks() only clears call history but keeps mock implementations. vi.restoreAllMocks() restores originals AND clears history."
🤖 Fix all issues with AI agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`:
- Around line 151-162: The test "highlights the currently selected option based
on currentLabel" only checks presence of the label and not the actual highlight;
update the test for RecurrenceDropdown to assert the real highlighting mechanism
used by the component (e.g., find the rendered option for currentLabel "Daily"
and expect it to have the active CSS class or aria attribute). Specifically, in
the test for RecurrenceDropdown with currentLabel="Daily" use
screen.getByText('Daily') to locate the element, then assert either
toHaveClass('active') or toHaveAttribute('aria-selected','true') (or whichever
the component implements), or rename the test to reflect mere presence if you
don't want to assert highlighting.
- Around line 84-85: After clicking the dropdown toggle
(user.click(screen.getByTestId('recurrence-toggle'))) you must wait for the
dropdown container/items to appear before interacting with them; update each
occurrence (the clicks at recurrence-item-1 and the similar cases at the other
two spots) to call waitFor from `@testing-library/react` to assert the menu or a
specific item is present/visible (e.g., waitFor(() =>
expect(screen.getByTestId('recurrence-item-1')).toBeInTheDocument())) before
calling user.click on recurrence-item-1; do this for the three failing spots so
the test no longer races with async rendering.
- Around line 190-194: The test triggers a race by selecting an item then
immediately reopening the dropdown; update the sequence around
getByTestId('recurrence-toggle') / 'recurrence-item-1' and 'recurrence-item-2'
to explicitly wait for the dropdown to close after the first selection and wait
for it to open after the second toggle click (use waitForElementToBeRemoved(()
=> screen.queryByTestId('recurrence-menu')) or await
screen.findByTestId('recurrence-menu') / waitFor(() =>
expect(screen.getByTestId('recurrence-menu')).toBeVisible()) as appropriate).
Concretely: after clicking 'recurrence-item-1' await the menu-remove wait, then
click the toggle and await the menu-open wait before clicking
'recurrence-item-2', and finally await the resulting close; reference test
elements 'recurrence-toggle', 'recurrence-item-1', 'recurrence-item-2' and the
dropdown test id (e.g. 'recurrence-menu') when making the changes.
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx
Show resolved
Hide resolved
| it('highlights the currently selected option based on currentLabel', () => { | ||
| renderWithI18n( | ||
| <RecurrenceDropdown | ||
| recurrenceOptions={mockOptions} | ||
| currentLabel="Daily" | ||
| onSelect={vi.fn()} | ||
| t={mockT} | ||
| />, | ||
| ); | ||
|
|
||
| expect(screen.getByText('Daily')).toBeInTheDocument(); | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Test name doesn't match assertion — no highlighting is verified.
The test claims to verify "highlights the currently selected option" but only asserts getByText('Daily').toBeInTheDocument(). This doesn't verify any visual highlighting (e.g., an active class, aria-selected, or style difference). Consider asserting on the actual highlighting mechanism (CSS class, attribute, etc.).
🤖 Prompt for AI Agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`
around lines 151 - 162, The test "highlights the currently selected option based
on currentLabel" only checks presence of the label and not the actual highlight;
update the test for RecurrenceDropdown to assert the real highlighting mechanism
used by the component (e.g., find the rendered option for currentLabel "Daily"
and expect it to have the active CSS class or aria attribute). Specifically, in
the test for RecurrenceDropdown with currentLabel="Daily" use
screen.getByText('Daily') to locate the element, then assert either
toHaveClass('active') or toHaveAttribute('aria-selected','true') (or whichever
the component implements), or rename the test to reflect mere presence if you
don't want to assert highlighting.
| await user.click(screen.getByTestId('recurrence-toggle')); | ||
| await user.click(screen.getByTestId('recurrence-item-1')); | ||
|
|
||
| await user.click(screen.getByTestId('recurrence-toggle')); | ||
| await user.click(screen.getByTestId('recurrence-item-2')); |
There was a problem hiding this comment.
Lines 190-191 click toggle and select an item, then lines 193-194 immediately re-click the toggle and select another item without waiting for the dropdown to close after the first selection and reopen after the second toggle click.
Proposed fix
await user.click(screen.getByTestId('recurrence-toggle'));
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrence-item-1')).toBeInTheDocument();
+ });
await user.click(screen.getByTestId('recurrence-item-1'));
+ // Wait for dropdown to close before re-opening
+ await waitFor(() => {
+ expect(screen.queryByTestId('recurrence-item-1')).not.toBeInTheDocument();
+ });
+
await user.click(screen.getByTestId('recurrence-toggle'));
+ await waitFor(() => {
+ expect(screen.getByTestId('recurrence-item-2')).toBeInTheDocument();
+ });
await user.click(screen.getByTestId('recurrence-item-2'));As per coding guidelines: "After clicking elements that close UI: MUST waitFor close completion... BEFORE re-opening" and "In loops testing multiple UI states: re-open → wait for open → interact → wait for result → wait for close."
🤖 Prompt for AI Agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`
around lines 190 - 194, The test triggers a race by selecting an item then
immediately reopening the dropdown; update the sequence around
getByTestId('recurrence-toggle') / 'recurrence-item-1' and 'recurrence-item-2'
to explicitly wait for the dropdown to close after the first selection and wait
for it to open after the second toggle click (use waitForElementToBeRemoved(()
=> screen.queryByTestId('recurrence-menu')) or await
screen.findByTestId('recurrence-menu') / waitFor(() =>
expect(screen.getByTestId('recurrence-menu')).toBeVisible()) as appropriate).
Concretely: after clicking 'recurrence-item-1' await the menu-remove wait, then
click the toggle and await the menu-open wait before clicking
'recurrence-item-2', and finally await the resulting close; reference test
elements 'recurrence-toggle', 'recurrence-item-1', 'recurrence-item-2' and the
dropdown test id (e.g. 'recurrence-menu') when making the changes.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx (1)
30-36:⚠️ Potential issue | 🔴 Critical❌ BLOCKING — Use
vi.restoreAllMocks()and addcleanup()inafterEach.Two issues in the test lifecycle hooks:
vi.clearAllMocks()only resets call history but keeps mock implementations, causing mock leakage across sharded tests. Replace withvi.restoreAllMocks().afterEachis missingcleanup()from@testing-library/react, which is required to tear down rendered components between tests.Also,
vi.restoreAllMocks()inafterEachmakes thebeforeEachclear redundant.Proposed fix
-import { render, screen, waitFor } from '@testing-library/react'; +import { render, screen, waitFor, cleanup } from '@testing-library/react';- beforeEach(() => { - vi.clearAllMocks(); - }); - afterEach(() => { - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); });As per coding guidelines: "❌ BLOCKING - Use vi.restoreAllMocks() at file:line" and "❌ BLOCKING - Missing required cleanup at file:line".
🤖 Fix all issues with AI agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`:
- Around line 153-164: The test currently checks that mockT was called with
'recurring' but not that the translation is applied to the DOM; update the test
for RecurrenceDropdown (renderWithI18n, mockT) to assert the rendered element's
aria-label attribute equals the expected translated string returned by mockT
(e.g., query the rendered button/input via getByRole or getByLabelText and
expect its aria-label to match mockT('recurring')), and remove or keep the mockT
call assertion if desired.
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
src/screens/UserPortal/Events/Events.spec.tsx (4)
840-844:⚠️ Potential issue | 🔴 Critical❌ BLOCKING — Use
vi.restoreAllMocks()and addcleanup()in afterEach.
vi.clearAllMocks()only resets call history but preserves mock implementations, causing leakage between sharded tests. Additionally,cleanup()from@testing-library/reactis missing.Proposed fix
+import { render, screen, waitFor, cleanup } from '@testing-library/react'; ... afterEach(() => { localStorage.clear(); - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); vi.useRealTimers(); });As per coding guidelines: "❌ BLOCKING - Use vi.restoreAllMocks() at file:line" and "❌ BLOCKING - Missing required cleanup at file:line". Based on learnings, enforce explicit cleanup of testing-library/react in test files.
811-817:⚠️ Potential issue | 🟠 Major🔴 HARDCODED DELAY —
wait()helper wrapssetTimeoutwith fixed durations.This helper is used ~15 times throughout the file (lines 864, 889, 996, 1113, 1159, 1184, 1214, etc.) and is the primary source of CI flakiness. Replace with
waitForassertions orfindBy*queries.As per coding guidelines: "🔴 HARDCODED DELAY at file:line — Replace with waitFor assertion".
227-238:⚠️ Potential issue | 🟠 Major🔴 NON-DETERMINISTIC TIMESTAMP and local timezone methods.
Line 227:
dayjs()captures current time beforevi.setSystemTimeruns inbeforeEach, soTEST_DATEis computed from the actual system clock at module load, not the mocked time. Lines 237–238 use.getMonth()and.getFullYear()(local timezone methods), which cause CI flakiness in non-UTC environments.Proposed fix
-const TEST_DATE = dayjs() - .year(2024) - .month(5) - .date(15) - .hour(8) - .minute(0) - .second(0) - .millisecond(0) - .toISOString(); -const dateObj = new Date(TEST_DATE); -const currentMonth = dateObj.getMonth(); -const currentYear = dateObj.getFullYear(); +const TEST_DATE = '2024-06-15T08:00:00.000Z'; +const dateObj = new Date(TEST_DATE); +const currentMonth = dateObj.getUTCMonth(); +const currentYear = dateObj.getUTCFullYear();As per coding guidelines: "🔴 NON-DETERMINISTIC TIMESTAMP — Replace with fixed UTC string" and "Flag ANY usage of local timezone methods as CRITICAL".
1819-1822:⚠️ Potential issue | 🟡 Minor🔴 NON-DETERMINISTIC TIMESTAMP and local timezone method at test scope.
new Date()on line 1820 andtoday.getDay()on line 1822 capture the real system time and use a local timezone method. Sincevi.setSystemTimeis set inbeforeEach,new Date()inside a test should return the mocked time, but.getDay()is still a local timezone method that can differ from UTC day.Proposed fix
- const today = new Date(); - const weekDayByJs = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; - const dayOfWeek = today.getDay(); + const today = new Date(TEST_DATE); + const weekDayByJs = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; + const dayOfWeek = today.getUTCDay();As per coding guidelines: "❌ NEVER use:
.getDay()" and "Use fixed UTC timestamps".src/screens/AdminPortal/OrganizationEvents/CreateEventModal.spec.tsx (3)
318-326:⚠️ Potential issue | 🔴 Critical❌ BLOCKING — Use
vi.restoreAllMocks()and addcleanup()in afterEach.Both
beforeEachandafterEachusevi.clearAllMocks()which only clears call history but keeps mock implementations. Additionally,cleanup()from@testing-library/reactis missing, risking DOM leakage between tests.Proposed fix
+import { render, screen, waitFor, cleanup } from '@testing-library/react'; ... beforeEach(() => { - vi.clearAllMocks(); + vi.restoreAllMocks(); mockUseMutation.mockReturnValue([mockCreate, { loading: false }]); mockValidateRecurrenceInput.mockReturnValue({ isValid: true, errors: [] }); }); afterEach(() => { - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); });As per coding guidelines: "❌ BLOCKING - Use vi.restoreAllMocks() at file:line" and "❌ BLOCKING - Missing required cleanup at file:line". Based on learnings, enforce explicit cleanup of testing-library/react in test files.
696-697:⚠️ Potential issue | 🟠 Major🔴 NON-DETERMINISTIC TIMESTAMP —
dayjs()without arguments captures current time.Lines 696–697 use
dayjs()(no arguments) to generate test dates, which captures the real clock. Same pattern at lines 936–939, 1129–1130, and 1290–1291. Use a fixed UTC string instead.Proposed fix
- const futureStart = dayjs().add(10, 'days').format('YYYY-MM-DD'); - const futureEnd = dayjs().add(11, 'days').format('YYYY-MM-DD'); + const futureStart = dayjs('2025-01-01T10:00:00Z').add(10, 'days').format('YYYY-MM-DD'); + const futureEnd = dayjs('2025-01-01T10:00:00Z').add(11, 'days').format('YYYY-MM-DD');As per coding guidelines: "🔴 NON-DETERMINISTIC TIMESTAMP at file:line — Replace with fixed UTC string". Apply the same fix to lines 936–939, 1129–1130, and 1290–1291.
1465-1488:⚠️ Potential issue | 🟠 Major🔴 NON-DETERMINISTIC TIMESTAMP inside fake-timer block.
Line 1467 uses
dayjs()without arguments. Even thoughvi.useFakeTimers()is active, the resultingnowvalue depends on when the test runs relative tosetSystemTimeat line 1471. Build the mock date from a fixed string instead.Proposed fix
it('sets default start date to today 00:00 UTC (standard case)', () => { - const now = dayjs(); - const mockDate = new Date( - Date.UTC(now.year(), now.month(), now.date(), 12, 0, 0), - ); + const mockDate = new Date('2025-06-15T12:00:00.000Z'); + const now = dayjs.utc(mockDate); vi.setSystemTime(mockDate);As per coding guidelines: "🔴 NON-DETERMINISTIC TIMESTAMP at file:line — Replace with fixed UTC string".
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx (1)
30-36:⚠️ Potential issue | 🔴 Critical❌ BLOCKING — Use
vi.restoreAllMocks()and addcleanup()in afterEach.Both hooks use
vi.clearAllMocks(). Per coding guidelines,vi.restoreAllMocks()is required to fully restore original implementations and prevent mock leakage across sharded tests. Also,cleanup()is missing.Proposed fix
+import { render, screen, waitFor, cleanup } from '@testing-library/react'; ... beforeEach(() => { - vi.clearAllMocks(); + vi.restoreAllMocks(); }); afterEach(() => { - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); });As per coding guidelines: "❌ BLOCKING - Use vi.restoreAllMocks() at file:line" and "❌ BLOCKING - Missing required cleanup at file:line". Based on learnings, enforce explicit cleanup of testing-library/react in test files.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #7286 +/- ##
===========================================
- Coverage 98.96% 98.96% -0.01%
===========================================
Files 478 478
Lines 18477 18491 +14
Branches 5907 5914 +7
===========================================
+ Hits 18286 18299 +13
- Misses 180 181 +1
Partials 11 11
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:
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx (1)
30-36:⚠️ Potential issue | 🔴 Critical❌ BLOCKING — Use
vi.restoreAllMocks()and addcleanup()inafterEach.Two violations:
vi.clearAllMocks()only resets call history but preserves mock implementations, causing leakage across sharded tests. Replace withvi.restoreAllMocks().afterEachis missingcleanup()from@testing-library/react, which is required to tear down rendered components between tests.Proposed fix
+import { cleanup, render, screen, waitFor } from '@testing-library/react'; -import { render, screen, waitFor } from '@testing-library/react'; ... beforeEach(() => { - vi.clearAllMocks(); + vi.restoreAllMocks(); }); afterEach(() => { - vi.clearAllMocks(); + cleanup(); + vi.restoreAllMocks(); });As per coding guidelines: "❌ BLOCKING - Use vi.restoreAllMocks() at file:line" and "❌ BLOCKING - Missing required cleanup at file:line".
🤖 Fix all issues with AI agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`:
- Around line 239-246: The mock DropDownButton in RecurrenceDropdown.spec.tsx
currently renders a <button> without an explicit type which defaults to
"submit"; update the mock default export (the anonymous component passed to
vi.doMock for 'shared-components/DropDownButton') so the rendered button
includes type="button" (i.e., <button data-testid="trigger-invalid"
type="button" ...>) to prevent accidental form submissions and satisfy lint
rules.
- Around line 267-269: The test performs an async user.click on
screen.getByTestId('trigger-invalid') and then immediately asserts
expect(onSelect).not.toHaveBeenCalled(), which can race; wrap the assertion in a
waitFor to ensure the post-click state settles. Specifically, after calling
user.click(screen.getByTestId('trigger-invalid')), replace the immediate
expect(...) with await waitFor(() => expect(onSelect).not.toHaveBeenCalled());
so the onSelect mock is asserted inside waitFor for stability.
- Around line 249-251: The afterEach block in RecurrenceDropdown.spec.tsx
currently only calls vi.doUnmock('shared-components/DropDownButton'); update
that afterEach to also call cleanup() from `@testing-library/react` and
vi.restoreAllMocks() so DOM and mocked spies are properly reset between tests;
locate the afterEach in the RecurrenceDropdown.spec.tsx describe block and
append calls to cleanup() and vi.restoreAllMocks() alongside the existing
vi.doUnmock call.
| vi.doMock('shared-components/DropDownButton', () => ({ | ||
| __esModule: true, | ||
| default: ({ onSelect }: { onSelect: (value: string) => void }) => ( | ||
| <button data-testid="trigger-invalid" onClick={() => onSelect('999')}> | ||
| Trigger Invalid | ||
| </button> | ||
| ), | ||
| })); |
There was a problem hiding this comment.
Add type="button" to the mock button element.
Biome flags this as an error — a <button> without an explicit type defaults to submit, which can cause unintended form submissions. Even in test mocks, this can affect behavior and will fail lint checks.
Proposed fix
- <button data-testid="trigger-invalid" onClick={() => onSelect('999')}>
+ <button type="button" data-testid="trigger-invalid" onClick={() => onSelect('999')}>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| vi.doMock('shared-components/DropDownButton', () => ({ | |
| __esModule: true, | |
| default: ({ onSelect }: { onSelect: (value: string) => void }) => ( | |
| <button data-testid="trigger-invalid" onClick={() => onSelect('999')}> | |
| Trigger Invalid | |
| </button> | |
| ), | |
| })); | |
| vi.doMock('shared-components/DropDownButton', () => ({ | |
| __esModule: true, | |
| default: ({ onSelect }: { onSelect: (value: string) => void }) => ( | |
| <button type="button" data-testid="trigger-invalid" onClick={() => onSelect('999')}> | |
| Trigger Invalid | |
| </button> | |
| ), | |
| })); |
🧰 Tools
🪛 Biome (2.3.14)
[error] 242-242: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🤖 Prompt for AI Agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`
around lines 239 - 246, The mock DropDownButton in RecurrenceDropdown.spec.tsx
currently renders a <button> without an explicit type which defaults to
"submit"; update the mock default export (the anonymous component passed to
vi.doMock for 'shared-components/DropDownButton') so the rendered button
includes type="button" (i.e., <button data-testid="trigger-invalid"
type="button" ...>) to prevent accidental form submissions and satisfy lint
rules.
| afterEach(() => { | ||
| vi.doUnmock('shared-components/DropDownButton'); | ||
| }); |
There was a problem hiding this comment.
❌ BLOCKING — Edge-case afterEach missing cleanup() and vi.restoreAllMocks().
Same cleanup requirements apply to this describe block.
Proposed fix
afterEach(() => {
vi.doUnmock('shared-components/DropDownButton');
+ cleanup();
+ vi.restoreAllMocks();
});As per coding guidelines: "❌ BLOCKING - Missing required cleanup at file:line".
🤖 Prompt for AI Agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`
around lines 249 - 251, The afterEach block in RecurrenceDropdown.spec.tsx
currently only calls vi.doUnmock('shared-components/DropDownButton'); update
that afterEach to also call cleanup() from `@testing-library/react` and
vi.restoreAllMocks() so DOM and mocked spies are properly reset between tests;
locate the afterEach in the RecurrenceDropdown.spec.tsx describe block and
append calls to cleanup() and vi.restoreAllMocks() alongside the existing
vi.doUnmock call.
| await user.click(screen.getByTestId('trigger-invalid')); | ||
|
|
||
| expect(onSelect).not.toHaveBeenCalled(); |
There was a problem hiding this comment.
🔴 RACE CONDITION — Assertion after user.click not inside waitFor.
Line 269 asserts immediately after an async click without waitFor. While asserting "not called" is less prone to flakiness, the coding guidelines require all post-click assertions to be wrapped.
Proposed fix
await user.click(screen.getByTestId('trigger-invalid'));
- expect(onSelect).not.toHaveBeenCalled();
+ await waitFor(() => {
+ expect(onSelect).not.toHaveBeenCalled();
+ });As per coding guidelines: "❌ BLOCKING - Race condition — Move assertion inside waitFor".
🤖 Prompt for AI Agents
In
`@src/shared-components/EventForm/RecurrenceDropdown/RecurrenceDropdown.spec.tsx`
around lines 267 - 269, The test performs an async user.click on
screen.getByTestId('trigger-invalid') and then immediately asserts
expect(onSelect).not.toHaveBeenCalled(), which can race; wrap the assertion in a
waitFor to ensure the post-click state settles. Specifically, after calling
user.click(screen.getByTestId('trigger-invalid')), replace the immediate
expect(...) with await waitFor(() => expect(onSelect).not.toHaveBeenCalled());
so the onSelect mock is asserted inside waitFor for stability.
|
@coderabbitai full review and reevaluate all your previous comments that may have been dismissed, ignored or resolved. Evaluate whether the goals of the issue linked in the first PR comment are met. If there are test cases, ensure that the updated or created tests won't be flaky, especially in a sharded testing environment where test cleanup is important and response times are highly variable. Post your conclusion as an easily readable comment that also includes your findings outside the diffs. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 38743 🏁 Script executed: Length of output: 30199 🏁 Script executed: Length of output: 136 🏁 Script executed: Length of output: 67499 🏁 Script executed: Length of output: 2662
✅ What Was Successfully CompletedRecurrenceDropdown Component
Test Coverage
Code Quality
|
palisadoes
left a comment
There was a problem hiding this comment.
Please address these extensive issues:
|
|
@coderabbitai full review and reevaluate all your previous comments that may have been dismissed, ignored or resolved. Evaluate whether the goals of the issue linked in the first PR comment are met. If there are test cases, ensure that the updated or created tests won't be flaky, especially in a sharded testing environment where test cleanup is important and response times are highly variable. Post your conclusion as an easily readable comment that also includes your findings outside the diffs. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 8214 🏁 Script executed: Length of output: 18057 🏁 Script executed: Length of output: 2616 🏁 Script executed: Length of output: 15804 🏁 Script executed: Length of output: 46121
✅ Issue
|
| Criterion | Status |
|---|---|
| Components use shared DropDownButton | ✅ Yes (directly or via abstraction) |
| Existing functionality preserved | ✅ Yes |
| All tests pass | ✅ Yes (per PR description) |
| No TypeScript errors/warnings | ✅ Yes (per PR description) |
| No ESLint errors/warnings | ✅ Yes (per PR description) |
| Accessibility maintained | ✅ Yes (ARIA labels, keyboard navigation) |
| Visual appearance matches design | ✅ Yes (same DropDownButton styles) |
| Test coverage ≥ 95% | ✅ Yes (per PR description) |
📋 Summary
What Changed in This PR:
- ✅ Refactored
RecurrenceDropdownto useDropDownButton - ✅ Removed
isOpen/onTogglefromRecurrenceDropdownAPI - ✅ Expanded test coverage (RecurrenceDropdown.spec.tsx: +154 lines)
- ✅ Fixed test cleanup issues (added
cleanup()+vi.restoreAllMocks()) - ✅ Updated test selectors across 3 screen test files to match new component structure
- ✅ Removed one obsolete test from EventListCardPreviewModal.spec.tsx
Code Quality:
- ✅ Proper memoization and hooks usage
- ✅ Type-safe with
InterfaceDropDownOptiontypes - ✅ Accessibility preserved (ARIA labels, keyboard navigation)
- ✅ No TypeScript or ESLint violations
Test Quality:
- ✅ Comprehensive coverage (edge cases, i18n, accessibility)
- ✅ Proper async handling (
waitFor,act,userEvent) - ✅ Shard-safe cleanup (DOM + mocks properly reset)
⚠️ Minor improvement opportunity: Edge-caseafterEachcould add full cleanup (non-blocking)
🎯 Final Verdict: APPROVE ✅
This PR successfully achieves all objectives of issue #6843. The refactor is clean, well-tested, and maintains backward compatibility. The author's clarification about SortingButton being a valid abstraction over DropDownButton is correct and appropriate for the codebase architecture.
Recommendation: Merge ✅
The test cleanup fixes have been verified, and the tests are now shard-safe with proper lifecycle management. All acceptance criteria are met.
✏️ Learnings added
Learnt from: palisadoes
URL: https://github.com/PalisadoesFoundation/talawa-admin/pull/7286
Timestamp: 2026-02-15T22:36:30.315Z
Learning: SortingButton is a wrapper component over DropDownButton in the talawa-admin codebase, used for sorting/filtering UI. It's a valid abstraction and doesn't need to be replaced with DropDownButton directly when migrating components.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
c7332cb
into
PalisadoesFoundation:develop
…alisadoesFoundation#7286) * refactor: use shared DropDownButton across Event Components * fix: pre-merge checks fixed * fix: race condition issue * fix: resolve race conditions in RecurrenceDropdown tests and update test IDs * test: verify recurrence toggle has correct aria-label attribute * test: use data-testid for selecting recurrence options in CreateEventModal tests * test: increased code coverage * test: fixed suggested issues * fix: missing cleanup
What kind of change does this PR introduce?
Refactor EventListCard components to use the shared DropDownButton
Issue Number:
Fixes #6843
Does this PR introduce a breaking change?
No
Checklist
CodeRabbit AI Review
Test Coverage
Other information
Have you read the contributing guide?
Yes
Summary by CodeRabbit
Refactor
Tests