Skip to content

Conversation

@dhirukumar
Copy link
Contributor

Description

This PR updates the Light Mode toggle icon in the UI.

What Was Changed

Replaced the old sun/gear-style icon (which looked like a settings icon):
Added the new light bulb icon (more intuitive for Light Mode):

before
Screenshot 2025-11-26 at 7 20 54 PM

After
Screenshot 2025-11-26 at 7 20 41 PM

🧠 Why This Change

The previous icon was visually misleading and confused users as it closely resembled the settings icon.
The new icon provides clearer UX and better represents Light Mode functionality.

🧪 Testing

Verified UI behavior in both Light and Dark modes
Ensured toggle works with no functional regressions

📝 Checklist

I've read and followed the contributing guidelines
I've run make check-test locally; all checks and tests passed.

Closes #2713

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

Caution

Review failed

The pull request is closed.

Summary by CodeRabbit

  • New Features

    • Implemented skeleton loading placeholders for snapshot grids to improve perceived load time.
    • Updated snapshot action button label from "View Details" to "View Snapshot" for improved clarity.
  • Style

    • Updated dark mode toggle icon to a lightbulb for enhanced visual consistency.
    • Replaced snapshot loading spinner with skeleton placeholders for better loading feedback.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Replaced the dark-mode icon in ModeToggle (sun → lightbulb), added snapshot skeleton rendering and stable keys in SkeletonsBase, replaced LoadingSpinner with SnapshotSkeleton in snapshots page, and updated unit tests to match these UI changes and async waits.

Changes

Cohort / File(s) Summary
Mode toggle component
frontend/src/components/ModeToggle.tsx
Swap imported icon from faSun to faLightbulb; conditional rendering now shows lightbulb when theme is 'dark'. No logic or event-handler changes.
ModeToggle tests
frontend/__tests__/unit/components/ModeToggle.test.tsx
Update test expectations to look for lightbulb icon (was sun); tests use async wait adjustments and mock FontAwesome rendering.
Skeletons & snapshot skeleton
frontend/src/components/SkeletonsBase.tsx, frontend/src/components/skeletons/SnapshotSkeleton.tsx
Import SnapshotSkeleton; add snapshotCardRender() and a snapshots branch in skeleton switch; change UserCardSkeleton keys to stable template user-skeleton-${index}.
Snapshots page (loading UI & label)
frontend/src/app/snapshots/page.tsx
Replace LoadingSpinner early return with conditional render of 12 SnapshotSkeleton items when loading; change snapshot card action label from "View Details" to "View Snapshot".
Home page test (async counters)
frontend/__tests__/unit/pages/Home.test.tsx
Replace fixed timeout with waitFor (3s) for animated counters; relax substring/match expectations (remove reliance on trailing plus).
Snapshots test (loading indicator)
frontend/__tests__/unit/pages/Snapshots.test.tsx
Change loading assertions from selecting by alt text "Loading indicator" (exact count) to querying by role status and expecting at least one element.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review FontAwesome import to ensure faLightbulb is available in the used icon pack.
  • Verify FontAwesomeIcon test mock behavior doesn't mask icon rendering issues.
  • Confirm SnapshotSkeleton rendering/markup matches previous LoadingSpinner expectations and accessibility roles relied on by tests.
  • Check no other code depended on numeric skeleton keys.

Possibly related PRs

Suggested reviewers

  • arkid15r
  • kasya

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: replacing the misleading light mode icon with a clearer one, which aligns with the primary objective.
Description check ✅ Passed The description clearly explains the icon change from sun/gear-style to light bulb, includes before/after screenshots, rationale, testing verification, and references the linked issue #2713.
Linked Issues check ✅ Passed The PR successfully implements the requirement from #2713 by replacing the misleading gear-like icon with a light bulb icon that clearly indicates theme switching rather than settings.
Out of Scope Changes check ✅ Passed The PR includes minimal related changes: icon replacement in ModeToggle, corresponding test updates with new test IDs and mocking, and a minor key prop fix in SkeletonsBase for stable rendering.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 922c294 and 2a8d202.

📒 Files selected for processing (5)
  • frontend/__tests__/unit/components/ModeToggle.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Home.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Snapshots.test.tsx (1 hunks)
  • frontend/src/app/snapshots/page.tsx (3 hunks)
  • frontend/src/components/SkeletonsBase.tsx (2 hunks)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@dhirukumar Like the icon 👌🏼 , but you also need to fix tests.

Please run make check-test locally and address found issues

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
frontend/__tests__/unit/components/ModeToggle.test.tsx (1)

10-36: Button and icon mocks align with ModeToggle usage; minor future‑drift risk in prop typing

The Button mock now correctly passes className through to the underlying <button> and accepts isIconOnly, and the FontAwesomeIcon mock exposing data-testid="icon-..." is a clean way to assert on the rendered icon names. The only potential concern is that the custom prop shape for the mocked Button can drift from the real @heroui/button API over time (e.g., if the real component adds required props). If this becomes an issue, consider deriving the props from the real component via jest.requireActual + React.ComponentProps<typeof RealButton> rather than duplicating a partial signature here.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f81dbd6 and 739ea36.

📒 Files selected for processing (2)
  • frontend/__tests__/unit/components/ModeToggle.test.tsx (5 hunks)
  • frontend/src/components/SkeletonsBase.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • frontend/src/components/SkeletonsBase.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-17T16:47:05.578Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:427-427
Timestamp: 2025-11-17T16:47:05.578Z
Learning: In the frontend test files for the OWASP/Nest repository, `expect(true).toBe(true)` no-op assertions may be intentionally added as workarounds when ESLint rule jest/expect-expect doesn't detect expectations inside helper functions or waitFor callbacks. These can be resolved by configuring the ESLint rule's assertFunctionNames option to recognize custom assertion helpers instead of flagging them as redundant.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-11-17T17:30:42.139Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:169-171
Timestamp: 2025-11-17T17:30:42.139Z
Learning: In the OWASP/Nest frontend tests (PR #2671 context), wrapper functions like `expectChaptersCountEqualsThree` that simply call another helper with a fixed parameter (e.g., `expectChaptersCountEquals(3)`) are intentionally used to avoid arrow function callbacks in `waitFor` calls. This pattern prevents adding nesting depth that would trigger rule typescript:S2004. Example: `await waitFor(expectChaptersCountEqualsThree)` avoids the extra nesting from `await waitFor(() => expectChaptersCountEquals(3))`.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-06-21T12:21:32.372Z
Learnt from: ahmedxgouda
Repo: OWASP/Nest PR: 1633
File: frontend/src/components/GradientRadialChart.tsx:67-116
Timestamp: 2025-06-21T12:21:32.372Z
Learning: The react-apexcharts Chart component does not support ARIA attributes like aria-label and role as direct props. To add accessibility attributes to ApexCharts in React, wrap the Chart component in a container div with the appropriate ARIA attributes.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
🧬 Code graph analysis (1)
frontend/__tests__/unit/components/ModeToggle.test.tsx (1)
frontend/src/components/ModeToggle.tsx (1)
  • ModeToggle (7-40)
🔇 Additional comments (3)
frontend/__tests__/unit/components/ModeToggle.test.tsx (3)

41-77: Icon rendering tests correctly validate the new light/dark icon mapping

The added beforeEach(jest.clearAllMocks) keeps theme mocks isolated between tests, and the first two tests now clearly assert the intended behavior: light theme shows the moon icon and dark theme shows the lightbulb icon via icon-moon / icon-lightbulb test IDs. This directly exercises the UX change described in the PR and should catch regressions if the icons or aria‑labels are altered.


79-117: Theme toggle interaction tests remain accurate and focused

Both interaction tests correctly verify that clicking the button calls setTheme with the opposite theme ('dark' from light, 'light' from dark), which matches ModeToggle’s logic. Using a dedicated setThemeMock per test keeps expectations clear and avoids cross‑test leakage.


119-129: Based on my verification through web search, I can confirm the technical foundation of the original review comment:

React Testing Library does wrap render() in act(), which flushes synchronous effects immediately. This means by the time the test can inspect the DOM, any synchronous useEffect that sets mounted to true will have already executed. The reviewer's concern about test fragility is technically sound.

However, I cannot fully verify:

  1. The exact implementation of the ModeToggle component
  2. Whether this test is currently passing or failing
  3. The full test file context and setup

The "mounted guard" pattern is indeed a legitimate SSR/hydration safety measure rather than a user-observable intermediate state, which supports the reviewer's recommendation. Since I cannot access the repository to definitively confirm the component code and current test status, I must mark this for manual verification:


Remove the "should not render before mounted" test—it attempts to observe a pre-effect state that RTL's act() wrapper prevents from being observable

React Testing Library wraps render() in act(), which flushes synchronous useEffect callbacks before returning. By the time this test can inspect container.firstChild, any synchronous effect setting mounted to true has already run. Since the mounted guard exists to prevent SSR hydration mismatches (not to create a visible intermediate state), this test cannot reliably observe the pre-mounted condition and should be removed.

-  test('should not render before mounted', () => {
-    useThemeMock.mockReturnValue({
-      theme: 'light',
-      setTheme: jest.fn(),
-    })
-
-    const { container } = render(<ModeToggle />)
-
-    // Component returns null initially (before useEffect runs)
-    expect(container.firstChild).toBeNull()
-  })

@dhirukumar dhirukumar force-pushed the change-lightmode-icon branch from 9c23622 to 5aa6388 Compare November 27, 2025 04:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
frontend/__tests__/unit/components/ModeToggle.test.tsx (2)

52-58: Consider wrapping icon assertion in waitFor for consistency.

While the button assertion is correctly wrapped in waitFor, the icon query on line 57 is outside the async block. Although the icon should be available when the button is rendered, wrapping both assertions in the same waitFor would be more consistent with the async pattern and could prevent potential flakiness.

-    await waitFor(() => {
-      const button = screen.getByRole('button', { name: /enable dark mode/i })
-      expect(button).toBeInTheDocument()
-    })
-
-    const icon = screen.getByTestId('icon-moon')
-    expect(icon).toBeInTheDocument()
+    await waitFor(() => {
+      const button = screen.getByRole('button', { name: /enable dark mode/i })
+      expect(button).toBeInTheDocument()
+      const icon = screen.getByTestId('icon-moon')
+      expect(icon).toBeInTheDocument()
+    })

69-75: Consider wrapping icon assertion in waitFor for consistency.

Same pattern as the light mode test: the icon query should ideally be inside the waitFor block alongside the button assertion for consistency.

-    await waitFor(() => {
-      const button = screen.getByRole('button', { name: /enable light mode/i })
-      expect(button).toBeInTheDocument()
-    })
-
-    const icon = screen.getByTestId('icon-lightbulb')
-    expect(icon).toBeInTheDocument()
+    await waitFor(() => {
+      const button = screen.getByRole('button', { name: /enable light mode/i })
+      expect(button).toBeInTheDocument()
+      const icon = screen.getByTestId('icon-lightbulb')
+      expect(icon).toBeInTheDocument()
+    })
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89caf92 and 5aa6388.

📒 Files selected for processing (2)
  • frontend/__tests__/unit/components/ModeToggle.test.tsx (5 hunks)
  • frontend/src/components/SkeletonsBase.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/SkeletonsBase.tsx
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-17T16:47:05.578Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:427-427
Timestamp: 2025-11-17T16:47:05.578Z
Learning: In the frontend test files for the OWASP/Nest repository, `expect(true).toBe(true)` no-op assertions may be intentionally added as workarounds when ESLint rule jest/expect-expect doesn't detect expectations inside helper functions or waitFor callbacks. These can be resolved by configuring the ESLint rule's assertFunctionNames option to recognize custom assertion helpers instead of flagging them as redundant.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-11-17T17:30:42.139Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:169-171
Timestamp: 2025-11-17T17:30:42.139Z
Learning: In the OWASP/Nest frontend tests (PR #2671 context), wrapper functions like `expectChaptersCountEqualsThree` that simply call another helper with a fixed parameter (e.g., `expectChaptersCountEquals(3)`) are intentionally used to avoid arrow function callbacks in `waitFor` calls. This pattern prevents adding nesting depth that would trigger rule typescript:S2004. Example: `await waitFor(expectChaptersCountEqualsThree)` avoids the extra nesting from `await waitFor(() => expectChaptersCountEquals(3))`.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-06-21T12:21:32.372Z
Learnt from: ahmedxgouda
Repo: OWASP/Nest PR: 1633
File: frontend/src/components/GradientRadialChart.tsx:67-116
Timestamp: 2025-06-21T12:21:32.372Z
Learning: The react-apexcharts Chart component does not support ARIA attributes like aria-label and role as direct props. To add accessibility attributes to ApexCharts in React, wrap the Chart component in a container div with the appropriate ARIA attributes.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
🧬 Code graph analysis (1)
frontend/__tests__/unit/components/ModeToggle.test.tsx (1)
frontend/src/components/ModeToggle.tsx (1)
  • ModeToggle (7-40)
🔇 Additional comments (5)
frontend/__tests__/unit/components/ModeToggle.test.tsx (5)

1-1: LGTM! Added waitFor for async test assertions.

The addition of waitFor to the imports is appropriate for handling asynchronous rendering behavior in the updated tests.


31-35: LGTM! FontAwesomeIcon mock enables icon verification.

The mock correctly extracts icon.iconName from FontAwesome icon objects and creates test IDs, enabling the tests to verify which icon (moon or lightbulb) is rendered.


40-42: LGTM! Proper test isolation.

The beforeEach hook ensures each test starts with a clean slate by clearing all mocks.


78-96: LGTM! Click interaction test properly handles async rendering.

The test correctly waits for the button to be rendered before interacting with it and verifies the theme toggle behavior.


98-116: LGTM! Click interaction test properly handles async rendering.

The test correctly waits for the button to be rendered before interacting with it and verifies the theme toggle behavior from dark to light mode.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
frontend/__tests__/unit/components/ModeToggle.test.tsx (1)

40-76: Light/dark render tests accurately exercise ARIA labels and icons

Clearing mocks in beforeEach plus using waitFor around the button queries gives you clean, deterministic tests even if ModeToggle’s mounted/theme logic ever becomes asynchronous. The expectations for:

  • light theme → “enable dark mode” + icon-moon
  • dark theme → “enable light mode” + icon-lightbulb

align well with the UX intent from the linked issue.

If you want to simplify slightly, you could replace the waitFor blocks with findByRole/findByTestId (e.g., const button = await screen.findByRole(...)), but the current approach is perfectly fine.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5aa6388 and 922c294.

📒 Files selected for processing (1)
  • frontend/__tests__/unit/components/ModeToggle.test.tsx (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-11-17T16:47:05.578Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:427-427
Timestamp: 2025-11-17T16:47:05.578Z
Learning: In the frontend test files for the OWASP/Nest repository, `expect(true).toBe(true)` no-op assertions may be intentionally added as workarounds when ESLint rule jest/expect-expect doesn't detect expectations inside helper functions or waitFor callbacks. These can be resolved by configuring the ESLint rule's assertFunctionNames option to recognize custom assertion helpers instead of flagging them as redundant.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-11-17T17:30:42.139Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:169-171
Timestamp: 2025-11-17T17:30:42.139Z
Learning: In the OWASP/Nest frontend tests (PR #2671 context), wrapper functions like `expectChaptersCountEqualsThree` that simply call another helper with a fixed parameter (e.g., `expectChaptersCountEquals(3)`) are intentionally used to avoid arrow function callbacks in `waitFor` calls. This pattern prevents adding nesting depth that would trigger rule typescript:S2004. Example: `await waitFor(expectChaptersCountEqualsThree)` avoids the extra nesting from `await waitFor(() => expectChaptersCountEquals(3))`.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
📚 Learning: 2025-06-21T12:21:32.372Z
Learnt from: ahmedxgouda
Repo: OWASP/Nest PR: 1633
File: frontend/src/components/GradientRadialChart.tsx:67-116
Timestamp: 2025-06-21T12:21:32.372Z
Learning: The react-apexcharts Chart component does not support ARIA attributes like aria-label and role as direct props. To add accessibility attributes to ApexCharts in React, wrap the Chart component in a container div with the appropriate ARIA attributes.

Applied to files:

  • frontend/__tests__/unit/components/ModeToggle.test.tsx
🔇 Additional comments (3)
frontend/__tests__/unit/components/ModeToggle.test.tsx (3)

10-35: Mocks for Button and FontAwesomeIcon are well-shaped for these tests

The @heroui/button mock now correctly accepts className and forwards it, and the FontAwesomeIcon mock exposing data-testid="icon-${iconName}" neatly decouples assertions from FontAwesome internals while still using real iconName values. This should make the tests less brittle around styling and icon implementation details.


78-116: Theme toggle interaction tests correctly cover both directions

The click tests for switching:

  • light → dark (expect(setThemeMock).toHaveBeenCalledWith('dark'))
  • dark → light (...With('light'))

are straightforward and correctly wrap the initial render in waitFor to ensure the button is present before interacting. This gives good coverage that the control actually drives setTheme with the expected target theme.


118-129: New “render after mount” test now matches RTL/React effect timing

The should render after mount test correctly asserts that ModeToggle is rendered (non‑null container.firstChild and a visible button) immediately after render, which matches React Testing Library’s behavior of flushing useEffect within act. This replaces the earlier, incorrect “pre‑mount” expectation and should prevent the prior false‑negative test behavior.

@dhirukumar dhirukumar closed this Nov 27, 2025
@dhirukumar dhirukumar deleted the change-lightmode-icon branch November 27, 2025 11:10
@sonarqubecloud
Copy link

@dhirukumar dhirukumar restored the change-lightmode-icon branch November 27, 2025 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Light Mode Icon Is Misleading and Confusing

2 participants