Skip to content

Conversation

@Picodes10
Copy link

Proposed change

Write unit tests for the React component to ensure expected behavior, edge case handling, and rendering logic.

  • Essential Test Coverage Checklist
  • Renders successfully with minimal required props
  • Conditional rendering logic
  • Prop-based behavior – different props affect output
  • Event handling – simulate user actions and verify callbacks
  • State changes / internal logic
  • Default values and fallbacks
  • Text and content rendering
  • Handles edge cases and invalid inputs
  • Accessibility roles and labels
  • DOM structure / classNames / styles

Resolves #1885

Test Coverage Summary:

  • Rendering: Handles empty, single, and multiple repositories with minimal props.
  • Conditional Logic: Tests "Show more/less" visibility and behavior based on repo count.
  • Prop Behavior: Validates correct rendering with full, partial, and zero-valued props.
  • Event Handling: Simulates clicks on repositories and toggle buttons.
  • State Logic: Covers expansion/collapse and pagination state transitions.
  • Fallbacks: Handles missing optional props and zero counts gracefully.
  • Content Rendering: Confirms accurate display of names, stats, and labels.
  • Edge Cases: Supports null, undefined, or incomplete repository data.
  • Accessibility: Verifies ARIA roles, semantic HTML, and accessibility compliance.
  • Styling: Ensures correct classNames, layout structure, and responsiveness.

Checklist

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 6, 2025

Summary by CodeRabbit

  • Tests

    • Standardized import sources for testing utilities across all frontend test files, improving consistency and maintainability.
    • Significantly expanded and refactored the test suite for the RepositoriesCard component, adding extensive coverage for various scenarios, UI states, accessibility, and error handling.
    • Refactored and clarified test logic in several component and page test files for improved reliability and readability.
  • Chores

    • Added and reorganized development dependencies for cross-platform testing and utility support.
    • Introduced a script to automatically reorder import statements in test files.
    • Updated TypeScript configuration to support a new path alias for simplified imports.
    • Disabled a pre-commit YAML formatting hook due to compatibility issues.
    • Minor configuration and setup improvements for test environment consistency.

Walkthrough

The test suite for the ActionButton component was removed and replaced with comprehensive tests for the RepositoriesCard component, covering a wide range of scenarios and behaviors. Additionally, the RepositoriesCard test suite was refactored and expanded. Minor refactors and utility exports were made in other test files and setup scripts, and new testing dependencies were added or reorganized.

Changes

Cohort / File(s) Change Summary
RepositoriesCard Test Overhaul
frontend/__tests__/unit/components/ActionButton.test.tsx, frontend/__tests__/unit/components/RepositoriesCard.test.tsx
The ActionButton test suite was completely replaced with a new, comprehensive test suite for RepositoriesCard. The existing RepositoriesCard tests were extensively refactored, adding detailed coverage for rendering, props, event handling, state, edge cases, accessibility, and DOM structure.
Test Utility Exports and Imports
frontend/src/wrappers/testUtil.tsx
Expanded imports from testing libraries and updated exports to include multiple testing utilities (render, screen, fireEvent, waitFor, within, cleanup).
Test Setup Adjustment
frontend/jest.setup.ts
Removed mocking of console.error in the Jest setup, allowing errors to be logged normally during tests.
Test Dependency Management
frontend/package.json, package.json
Added cross-env and @testing-library/dom as dev dependencies, moved @testing-library/user-event to devDependencies, and updated the test script for cross-platform compatibility.
Test Refactors (Miscellaneous)
frontend/__tests__/unit/components/MetricsCard.test.tsx, frontend/__tests__/unit/pages/Home.test.tsx, frontend/__tests__/unit/pages/ProjectDetails.test.tsx
Refactored test logic by extracting assertions into helper functions or adding explicit type annotations. No changes to test logic or coverage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Assessment against linked issues

Objective Addressed Explanation
Renders successfully with minimal required props (#1885)
Conditional rendering logic (#1885)
Prop-based behavior – different props affect output (#1885)
Event handling – simulate user actions and verify callbacks (#1885)
State changes / internal logic (#1885)
Default values and fallbacks (#1885)
Text and content rendering (#1885)
Handles edge cases and invalid inputs (#1885)
Accessibility roles and labels (#1885)
DOM structure / classNames / styles (#1885)

Suggested reviewers

  • kasya
  • arkid15r

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0c340c and a693e6e.

📒 Files selected for processing (1)
  • frontend/package.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/package.json
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 5

🧹 Nitpick comments (3)
frontend/__tests__/unit/pages/Home.test.tsx (1)

366-366: Use arrow function for consistency

The callback function uses regular function syntax while the rest of the code uses arrow functions for consistency.

-  await waitFor(function () {
+  await waitFor(() => {
frontend/__tests__/unit/components/RepositoriesCard.test.tsx (1)

1-1: Remove unused imports

The userEvent from @testing-library/user-event is imported but never used in the test file.

-import { render, screen, fireEvent, waitFor } from '@testing-library/react'
+import { render, screen, fireEvent } from '@testing-library/react'

Also remove the unused userEvent import at line 2 if it's not needed.

frontend/__tests__/unit/components/ActionButton.test.tsx (1)

2-2: Remove unused import

The userEvent import is not used in this test file.

-import userEvent from '@testing-library/user-event'
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a89007 and d292c8f.

⛔ Files ignored due to path filters (2)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • frontend/__tests__/unit/components/ActionButton.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/MetricsCard.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Home.test.tsx (2 hunks)
  • frontend/__tests__/unit/pages/ProjectDetails.test.tsx (1 hunks)
  • frontend/jest.setup.ts (0 hunks)
  • frontend/package.json (3 hunks)
  • frontend/src/wrappers/testUtil.tsx (2 hunks)
  • package.json (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/jest.setup.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: Rajgupta36
PR: OWASP/Nest#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.
📚 Learning: in the detailscard component (frontend/src/components/carddetailspage.tsx), there's a length check b...
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/__tests__/unit/components/MetricsCard.test.tsx
📚 Learning: in the owasp nest project's barchart component (frontend/src/components/barchart.tsx), the days and ...
Learnt from: ahmedxgouda
PR: OWASP/Nest#1703
File: frontend/src/components/BarChart.tsx:33-46
Timestamp: 2025-07-03T03:08:03.290Z
Learning: In the OWASP Nest project's BarChart component (frontend/src/components/BarChart.tsx), the days and requirements arrays are guaranteed to always have the same length in their use cases, so input validation for array length matching is not needed.

Applied to files:

  • frontend/__tests__/unit/components/MetricsCard.test.tsx
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx
  • frontend/__tests__/unit/components/ActionButton.test.tsx
📚 Learning: in the detailscard component (frontend/src/components/carddetailspage.tsx), there's a safety check t...
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/__tests__/unit/components/MetricsCard.test.tsx
📚 Learning: when testing react page components that use mocked form components, validation logic should be teste...
Learnt from: Rajgupta36
PR: OWASP/Nest#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/MetricsCard.test.tsx
  • frontend/src/wrappers/testUtil.tsx
  • frontend/__tests__/unit/pages/Home.test.tsx
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx
  • frontend/__tests__/unit/components/ActionButton.test.tsx
🔇 Additional comments (6)
package.json (1)

1-6: LGTM! Proper dependency addition for enhanced testing utilities.

The addition of @testing-library/dom as a dev dependency correctly supports the expanded testing utilities now exported from testUtil.tsx, particularly the within function for scoped DOM queries.

frontend/__tests__/unit/pages/ProjectDetails.test.tsx (1)

161-161: LGTM! Minor type annotation improvement.

Adding the explicit void return type annotation improves type clarity for the waitFor callback.

frontend/package.json (2)

15-15: LGTM! Cross-platform compatibility improvement.

Using cross-env ensures the NODE_OPTIONS environment variable is set correctly across different operating systems.


74-74: LGTM! Proper dependency organization.

Moving @testing-library/user-event to devDependencies and adding cross-env as a dev dependency correctly organizes packages based on their usage context.

Also applies to: 86-86

frontend/src/wrappers/testUtil.tsx (1)

2-3: LGTM! Enhanced testing utility exports.

Centralizing commonly used testing utilities (screen, fireEvent, waitFor, within, cleanup) in the test wrapper improves developer experience and maintains consistency across test files.

Also applies to: 13-13

frontend/__tests__/unit/components/RepositoriesCard.test.tsx (1)

283-283: Ignore this comment: the repositories prop is already optional in the TypeScript definition
The RepositoriesCardProps type in src/types/project.ts declares repositories?: RepositoryCardProps[], so testing with repositories={undefined} correctly reflects the component’s contract. No changes needed to the test or types.

Likely an incorrect or invalid review comment.

Comment on lines 1 to 536
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as nextNavigation from 'next/navigation'
import RepositoriesCard from 'components/RepositoriesCard'
import type { RepositoryCardProps } from 'types/project'

jest.mock('@heroui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
// Mock next/navigation
jest.mock('next/navigation', () => ({
useRouter: jest.fn(),
}))

jest.mock('next/link', () => {
return ({
children,
href,
onClick,
}: {
children: React.ReactNode
href: string
onClick?: () => void
}) => (
<a
href={href}
onClick={(e) => {
e.preventDefault()
onClick?.()
}}
>
{children}
</a>
)
// Mock FontAwesome icons
jest.mock('@fortawesome/react-fontawesome', () => ({
FontAwesomeIcon: ({ icon, className }: { icon: any; className?: string }) => (
<span data-testid={`icon-${icon.iconName}`} className={className} />
),
}))

// Mock InfoItem component
jest.mock('components/InfoItem', () => {
return function MockInfoItem({
icon,
pluralizedName,
unit,
value
}: {
icon: any;
pluralizedName: string;
unit: string;
value: number
}) {
return (
<div data-testid={`info-item-${pluralizedName.toLowerCase()}`}>
<span data-testid={`icon-${icon.iconName}`} />
<span>{pluralizedName}: {value}</span>
</div>
)
}
})

describe('ActionButton', () => {
afterEach(() => {
cleanup()
// Mock TruncatedText component
jest.mock('components/TruncatedText', () => ({
TruncatedText: ({ text }: { text?: string }) => (
<span data-testid="truncated-text">{text}</span>
),
}))

describe('RepositoriesCard', () => {
const mockPush = jest.fn()

beforeEach(() => {
jest.clearAllMocks()
;(nextNavigation.useRouter as jest.Mock).mockReturnValue({
push: mockPush,
})
})

it('renders without crashing', () => {
render(<ActionButton>Test Button</ActionButton>)
expect(screen.getByText('Test Button')).toBeInTheDocument()
// Test data
const mockRepository: RepositoryCardProps = {
key: 'test-repo',
name: 'Test Repository',
starsCount: 100,
forksCount: 50,
contributorsCount: 25,
openIssuesCount: 10,
subscribersCount: 5,
url: 'https://github.com/test/repo',
organization: {
login: 'test-org',
createdAt: new Date('2020-01-01T00:00:00Z').getTime(),
updatedAt: new Date('2020-01-01T00:00:00Z').getTime(),
avatarUrl: '',
collaboratorsCount: 0,
followersCount: 0,
key: '',
name: '',
objectID: '',
publicRepositoriesCount: 0,
url: ''
},
}

const createMockRepositories = (count: number): RepositoryCardProps[] => {
return Array.from({ length: count }, (_, index) => ({
...mockRepository,
key: `repo-${index}`,
name: `Repository ${index + 1}`,
}))
}

describe('Renders successfully with minimal required props', () => {
it('renders without crashing with empty repositories array', () => {
render(<RepositoriesCard repositories={[]} />)
expect(screen.getByRole('generic')).toBeInTheDocument()
})

it('renders with single repository', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)
expect(screen.getByText('Test Repository')).toBeInTheDocument()
})

it('renders with multiple repositories', () => {
const repositories = createMockRepositories(3)
render(<RepositoriesCard repositories={repositories} />)

expect(screen.getByText('Repository 1')).toBeInTheDocument()
expect(screen.getByText('Repository 2')).toBeInTheDocument()
expect(screen.getByText('Repository 3')).toBeInTheDocument()
})
})

it('renders as a button when no URL is provided', () => {
render(<ActionButton>Click Me</ActionButton>)
expect(screen.getByRole('button')).toBeInTheDocument()
describe('Conditional rendering logic', () => {
it('shows only first 4 repositories by default', () => {
const repositories = createMockRepositories(6)
render(<RepositoriesCard repositories={repositories} />)

expect(screen.getByText('Repository 1')).toBeInTheDocument()
expect(screen.getByText('Repository 2')).toBeInTheDocument()
expect(screen.getByText('Repository 3')).toBeInTheDocument()
expect(screen.getByText('Repository 4')).toBeInTheDocument()
expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()
expect(screen.queryByText('Repository 6')).not.toBeInTheDocument()
})

it('shows "Show more" button when more than 4 repositories', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

expect(screen.getByRole('button', { name: /show more/i })).toBeInTheDocument()
})

it('does not show "Show more" button when 4 or fewer repositories', () => {
const repositories = createMockRepositories(4)
render(<RepositoriesCard repositories={repositories} />)

expect(screen.queryByRole('button', { name: /show more/i })).not.toBeInTheDocument()
})

it('shows all repositories when "Show more" is clicked', () => {
const repositories = createMockRepositories(6)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)

expect(screen.getByText('Repository 5')).toBeInTheDocument()
expect(screen.getByText('Repository 6')).toBeInTheDocument()
})

it('shows "Show less" button when all repositories are displayed', () => {
const repositories = createMockRepositories(6)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)

expect(screen.getByRole('button', { name: /show less/i })).toBeInTheDocument()
})

it('hides repositories when "Show less" is clicked', () => {
const repositories = createMockRepositories(6)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)

const showLessButton = screen.getByRole('button', { name: /show less/i })
fireEvent.click(showLessButton)

expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()
expect(screen.queryByText('Repository 6')).not.toBeInTheDocument()
})
})

it('renders as a link when URL is provided', () => {
render(<ActionButton url="https://example.com">Visit Site</ActionButton>)
const link = screen.getByRole('link')
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', 'https://example.com')
describe('Prop-based behavior', () => {
it('renders repository information correctly', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

expect(screen.getByTestId('info-item-stars')).toBeInTheDocument()
expect(screen.getByTestId('info-item-forks')).toBeInTheDocument()
expect(screen.getByTestId('info-item-contributors')).toBeInTheDocument()
expect(screen.getByTestId('info-item-issues')).toBeInTheDocument()
})

it('handles repositories with missing optional props', () => {
const repositoryWithoutKey = {
...mockRepository,
key: undefined,
}
render(<RepositoriesCard repositories={[repositoryWithoutKey]} />)

expect(screen.getByText('Test Repository')).toBeInTheDocument()
})

it('handles repositories with zero values', () => {
const repositoryWithZeros = {
...mockRepository,
starsCount: 0,
forksCount: 0,
contributorsCount: 0,
openIssuesCount: 0,
}
render(<RepositoriesCard repositories={[repositoryWithZeros]} />)

expect(screen.getByTestId('info-item-stars')).toBeInTheDocument()
expect(screen.getByTestId('info-item-forks')).toBeInTheDocument()
expect(screen.getByTestId('info-item-contributors')).toBeInTheDocument()
expect(screen.getByTestId('info-item-issues')).toBeInTheDocument()
})
})

it('displays different content based on children prop', () => {
render(<ActionButton>Custom Text</ActionButton>)
expect(screen.getByText('Custom Text')).toBeInTheDocument()
describe('Event handling', () => {
it('navigates to repository page when repository is clicked', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

const repositoryButton = screen.getByRole('button', { name: /test repository/i })
fireEvent.click(repositoryButton)

expect(mockPush).toHaveBeenCalledWith('/organizations/test-org/repositories/test-repo')
})

it('handles click on "Show more" button', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)

expect(screen.getByText('Repository 5')).toBeInTheDocument()
})

it('handles click on "Show less" button', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)

const showLessButton = screen.getByRole('button', { name: /show less/i })
fireEvent.click(showLessButton)

expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()
})
})

it('shows tooltip when tooltipLabel is provided', async () => {
render(<ActionButton tooltipLabel="Test Label">Test Button</ActionButton>)
await waitFor(() => {
const interactiveElement = screen.getByRole('button')
expect(interactiveElement).toHaveAttribute('aria-label', 'Test Label')
describe('State changes and internal logic', () => {
it('toggles showAllRepositories state correctly', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

// Initially shows first 4
expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()

// Click show more
const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)
expect(screen.getByText('Repository 5')).toBeInTheDocument()

// Click show less
const showLessButton = screen.getByRole('button', { name: /show less/i })
fireEvent.click(showLessButton)
expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()
})

it('maintains state correctly when toggling multiple times', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })

// Toggle multiple times
fireEvent.click(showMoreButton)
fireEvent.click(showMoreButton)
fireEvent.click(showMoreButton)

// Should still show all repositories
expect(screen.getByText('Repository 5')).toBeInTheDocument()
})
})

it('does not show tooltip when tooltipLabel is not provided', () => {
render(<ActionButton>Test Button</ActionButton>)
expect(screen.getByText('Test Button')).not.toHaveAttribute('data-tooltip-content')
describe('Default values and fallbacks', () => {
it('handles undefined repositories prop', () => {
render(<RepositoriesCard repositories={undefined} />)
expect(screen.getByRole('generic')).toBeInTheDocument()
})

it('handles empty repositories array', () => {
render(<RepositoriesCard repositories={[]} />)
expect(screen.getByRole('generic')).toBeInTheDocument()
})

it('handles repository with missing name', () => {
const repositoryWithoutName = {
...mockRepository,
name: '',
}
render(<RepositoriesCard repositories={[repositoryWithoutName]} />)

const truncatedText = screen.getByTestId('truncated-text')
expect(truncatedText).toHaveTextContent('')
})
})

it('calls onClick handler when button is clicked', async () => {
const mockOnClick = jest.fn()
render(<ActionButton onClick={mockOnClick}>Click me</ActionButton>)
await waitFor(() => {
fireEvent.click(screen.getByRole('button'))
expect(mockOnClick).toHaveBeenCalled()
describe('Text and content rendering', () => {
it('displays repository name correctly', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)
expect(screen.getByTestId('truncated-text')).toHaveTextContent('Test Repository')
})

it('displays correct button text for show more/less', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

expect(screen.getByRole('button', { name: /show more/i })).toBeInTheDocument()

const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)

expect(screen.getByRole('button', { name: /show less/i })).toBeInTheDocument()
})
})

it('calls onClick handler when link is clicked', async () => {
const mockOnClick = jest.fn()
render(
<ActionButton url="https://example.com" onClick={mockOnClick}>
Visit Site
</ActionButton>
)
await waitFor(() => {
fireEvent.click(screen.getByRole('link'))
expect(mockOnClick).toHaveBeenCalled()
describe('Edge cases and invalid inputs', () => {
it('handles very large number of repositories', () => {
const repositories = createMockRepositories(100)
render(<RepositoriesCard repositories={repositories} />)

// Should show first 4 by default
expect(screen.getByText('Repository 1')).toBeInTheDocument()
expect(screen.getByText('Repository 4')).toBeInTheDocument()
expect(screen.queryByText('Repository 5')).not.toBeInTheDocument()

// Should show all when expanded
const showMoreButton = screen.getByRole('button', { name: /show more/i })
fireEvent.click(showMoreButton)

expect(screen.getByText('Repository 100')).toBeInTheDocument()
})

it('handles repositories with very long names', () => {
const repositoryWithLongName = {
...mockRepository,
name: 'This is a very long repository name that should be truncated when displayed in the UI',
}
render(<RepositoriesCard repositories={[repositoryWithLongName]} />)

expect(screen.getByTestId('truncated-text')).toHaveTextContent(repositoryWithLongName.name)
})

it('handles repositories with special characters in names', () => {
const repositoryWithSpecialChars = {
...mockRepository,
name: 'repo-with-special-chars-!@#$%^&*()',
}
render(<RepositoriesCard repositories={[repositoryWithSpecialChars]} />)

expect(screen.getByTestId('truncated-text')).toHaveTextContent(repositoryWithSpecialChars.name)
})

it('handles repositories with very large numbers', () => {
const repositoryWithLargeNumbers = {
...mockRepository,
starsCount: 999999999,
forksCount: 888888888,
contributorsCount: 777777777,
openIssuesCount: 666666666,
}
render(<RepositoriesCard repositories={[repositoryWithLargeNumbers]} />)

expect(screen.getByTestId('info-item-stars')).toBeInTheDocument()
expect(screen.getByTestId('info-item-forks')).toBeInTheDocument()
expect(screen.getByTestId('info-item-contributors')).toBeInTheDocument()
expect(screen.getByTestId('info-item-issues')).toBeInTheDocument()
})
})

it('handles missing children gracefully', () => {
render(<ActionButton tooltipLabel="Action button">{undefined}</ActionButton>)

const button = screen.getByRole('button')
expect(button).toBeInTheDocument()
try {
expect(button).toHaveAccessibleName('Action button')
} catch {
// eslint-disable-next-line jest/no-conditional-expect
expect(button).not.toHaveAccessibleName('')
}
describe('Accessibility roles and labels', () => {
it('has proper button roles for interactive elements', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

// Repository buttons
expect(screen.getByRole('button', { name: /repository 1/i })).toBeInTheDocument()

// Show more/less button
expect(screen.getByRole('button', { name: /show more/i })).toBeInTheDocument()
})

it('has accessible button labels', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton).toHaveAccessibleName(/show more/i)

fireEvent.click(showMoreButton)

const showLessButton = screen.getByRole('button', { name: /show less/i })
expect(showLessButton).toHaveAccessibleName(/show less/i)
})

it('has proper semantic structure', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

// Main container
expect(screen.getByRole('generic')).toBeInTheDocument()

// Repository button
expect(screen.getByRole('button', { name: /test repository/i })).toBeInTheDocument()
})
})

it('handles invalid url gracefully', () => {
render(<ActionButton url="">Test Button</ActionButton>)
expect(screen.getByRole('button')).toBeInTheDocument()
expect(screen.queryByRole('link')).not.toBeInTheDocument()
describe('DOM structure, classNames, and styles', () => {
it('has correct CSS classes for grid layout', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

const gridContainer = screen.getByRole('generic').querySelector('.grid')
expect(gridContainer).toHaveClass('grid-cols-1', 'gap-4', 'sm:grid-cols-2', 'md:grid-cols-3', 'lg:grid-cols-4')
})

it('has correct CSS classes for repository cards', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

const repositoryCard = screen.getByRole('button', { name: /test repository/i }).closest('div')
expect(repositoryCard).toHaveClass(
'h-46',
'flex',
'w-full',
'flex-col',
'gap-3',
'rounded-lg',
'border',
'p-4',
'shadow-sm',
'ease-in-out',
'hover:shadow-md',
'dark:border-gray-700',
'dark:bg-gray-800'
)
})

it('has correct CSS classes for show more/less button', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton).toHaveClass(
'mt-4',
'flex',
'items-center',
'justify-center',
'text-blue-400',
'hover:underline'
)
})

it('has correct container structure', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

const mainContainer = screen.getByRole('generic')
expect(mainContainer).toBeInTheDocument()

const gridContainer = mainContainer.querySelector('.grid')
expect(gridContainer).toBeInTheDocument()
})

it('has correct button container structure', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const buttonContainer = screen.getByRole('button', { name: /show more/i }).closest('.mt-6')
expect(buttonContainer).toHaveClass('mt-6', 'flex', 'items-center', 'justify-center', 'text-center')
})
})

describe('Integration with child components', () => {
it('renders InfoItem components with correct props', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

expect(screen.getByTestId('info-item-stars')).toBeInTheDocument()
expect(screen.getByTestId('info-item-forks')).toBeInTheDocument()
expect(screen.getByTestId('info-item-contributors')).toBeInTheDocument()
expect(screen.getByTestId('info-item-issues')).toBeInTheDocument()
})

it('renders TruncatedText component with repository name', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

const truncatedText = screen.getByTestId('truncated-text')
expect(truncatedText).toHaveTextContent('Test Repository')
})

it('renders FontAwesome icons correctly', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

// Check for chevron icons in show more/less button
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton.querySelector('[data-testid="icon-chevron-down"]')).toBeInTheDocument()

fireEvent.click(showMoreButton)

const showLessButton = screen.getByRole('button', { name: /show less/i })
expect(showLessButton.querySelector('[data-testid="icon-chevron-up"]')).toBeInTheDocument()
})
})

it('handles onClick without crashing when no handler is provided', () => {
render(<ActionButton url="https://example.com">Test Button</ActionButton>)
expect(() => {
fireEvent.click(screen.getByRole('link'))
}).not.toThrow()
describe('Error handling and robustness', () => {
it('handles navigation errors gracefully', () => {
mockPush.mockImplementation(() => {
throw new Error('Navigation error')
})

render(<RepositoriesCard repositories={[mockRepository]} />)

const repositoryButton = screen.getByRole('button', { name: /test repository/i })

// Should not crash when navigation fails
expect(() => fireEvent.click(repositoryButton)).not.toThrow()
})

it('handles missing organization data', () => {
render(<RepositoriesCard repositories={[{
...mockRepository,
organization: undefined,
}]} />)

fireEvent.click(screen.getByRole('button', { name: /test repository/i }))

// Should handle missing organization gracefully
expect(mockPush).toHaveBeenCalledWith('/organizations/undefined/repositories/test-repo')
})
})
})
})
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

File repurposed incorrectly - duplicate test coverage

This file ActionButton.test.tsx has been completely replaced with RepositoriesCard tests, which creates duplicate test coverage since RepositoriesCard.test.tsx already exists with nearly identical tests.

If the ActionButton component was removed, this test file should be deleted rather than repurposed. If ActionButton still exists, it needs its own tests.


🏁 Script executed:

#!/bin/bash
# Check if ActionButton component still exists
fd -e tsx -e ts "ActionButton" --exclude "*.test.*" --exclude "*.spec.*"

# Check for any imports of ActionButton
rg "import.*ActionButton" --glob "*.tsx" --glob "*.ts" --glob "!*.test.*" --glob "!*.spec.*"

Length of output: 377


ActionButton tests missing—file repurposed incorrectly

The ActionButton component still exists (frontend/src/components/ActionButton.tsx) and is imported by Modal.tsx and Card.tsx, but its dedicated test file has been completely overwritten with RepositoriesCard tests (which duplicate coverage in RepositoriesCard.test.tsx). To fix:

  • Rename or move the current frontend/__tests__/unit/components/ActionButton.test.tsx to RepositoriesCard.test.tsx (or the existing RepositoriesCard.test.tsx) so it isn’t falsely labeled.
  • Delete the repurposed ActionButton.test.tsx or relocate its contents to the proper RepositoriesCard test.
  • Create a new ActionButton.test.tsx alongside ActionButton.tsx that covers:
    • Rendering of the button
    • Click behavior and any callbacks/props
    • Disabled/enabled states and accessibility attributes
🤖 Prompt for AI Agents
In frontend/__tests__/unit/components/ActionButton.test.tsx (lines 1-536), the
file has been overwritten with RepositoriesCard tests, losing the original
ActionButton tests. To fix this, rename or move this file to
RepositoriesCard.test.tsx or merge its contents with the existing
RepositoriesCard test file to avoid duplication. Then delete or clear the
current ActionButton.test.tsx file and create a new test file named
ActionButton.test.tsx next to ActionButton.tsx that includes tests for
rendering, click behavior, callback handling, disabled/enabled states, and
accessibility attributes.

Comment on lines 493 to 507
it('renders FontAwesome icons correctly', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

// Check for chevron icons in show more/less button
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton.querySelector('[data-testid="icon-chevron-down"]')).toBeInTheDocument()

fireEvent.click(showMoreButton)

const showLessButton = screen.getByRole('button', { name: /show less/i })
expect(showLessButton.querySelector('[data-testid="icon-chevron-up"]')).toBeInTheDocument()
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid duplicate render calls in the same test

This test renders the component twice, which could cause unpredictable behavior.

     it('renders FontAwesome icons correctly', () => {
-      render(<RepositoriesCard repositories={[mockRepository]} />)
-      
-      // Check for chevron icons in show more/less button
       const repositories = createMockRepositories(5)
       render(<RepositoriesCard repositories={repositories} />)
📝 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.

Suggested change
it('renders FontAwesome icons correctly', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)
// Check for chevron icons in show more/less button
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)
const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton.querySelector('[data-testid="icon-chevron-down"]')).toBeInTheDocument()
fireEvent.click(showMoreButton)
const showLessButton = screen.getByRole('button', { name: /show less/i })
expect(showLessButton.querySelector('[data-testid="icon-chevron-up"]')).toBeInTheDocument()
})
it('renders FontAwesome icons correctly', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)
const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton.querySelector('[data-testid="icon-chevron-down"]')).toBeInTheDocument()
fireEvent.click(showMoreButton)
const showLessButton = screen.getByRole('button', { name: /show less/i })
expect(showLessButton.querySelector('[data-testid="icon-chevron-up"]')).toBeInTheDocument()
})
🤖 Prompt for AI Agents
In frontend/__tests__/unit/components/ActionButton.test.tsx around lines 493 to
507, the test renders the RepositoriesCard component twice, which can cause
unpredictable behavior. Refactor the test to render the component only once by
combining the necessary setup data (e.g., use createMockRepositories(5)) before
the single render call, then proceed with the assertions and interactions
without additional renders.

Comment on lines +96 to +102
newFunction()
expect(screen.getByText('55')).toBeInTheDocument()
expect(screen.getByRole('link')).toHaveAttribute('href', '/projects/dashboard/metrics/another')

function newFunction() {
expect(screen.getByText('Jan 1, 2024')).toBeInTheDocument()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Revert unnecessary function extraction.

The extraction of a simple assertion into newFunction reduces readability without providing any benefit. The generic function name adds no semantic value.

-    newFunction()
+    expect(screen.getByText('Jan 1, 2024')).toBeInTheDocument()
     expect(screen.getByText('55')).toBeInTheDocument()
     expect(screen.getByRole('link')).toHaveAttribute('href', '/projects/dashboard/metrics/another')
-
-    function newFunction() {
-      expect(screen.getByText('Jan 1, 2024')).toBeInTheDocument()
-    }
📝 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.

Suggested change
newFunction()
expect(screen.getByText('55')).toBeInTheDocument()
expect(screen.getByRole('link')).toHaveAttribute('href', '/projects/dashboard/metrics/another')
function newFunction() {
expect(screen.getByText('Jan 1, 2024')).toBeInTheDocument()
}
expect(screen.getByText('Jan 1, 2024')).toBeInTheDocument()
expect(screen.getByText('55')).toBeInTheDocument()
expect(screen.getByRole('link')).toHaveAttribute('href', '/projects/dashboard/metrics/another')
🤖 Prompt for AI Agents
In frontend/__tests__/unit/components/MetricsCard.test.tsx around lines 96 to
102, remove the extracted function named newFunction and place its assertion
directly in the test block. This means inline the expect statement checking for
'Jan 1, 2024' instead of calling a separate function with a generic name,
improving readability and clarity.

Comment on lines 493 to 507
expect(truncatedText).toHaveTextContent('Test Repository')
})

it('renders FontAwesome icons correctly', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)

// Check for chevron icons in show more/less button
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)

const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton.querySelector('[data-testid="icon-chevron-down"]')).toBeInTheDocument()

fireEvent.click(showMoreButton)

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid duplicate render calls in the same test

This test renders the component twice - once with mockRepository and then again with repositories. This could lead to unpredictable test behavior.

     it('renders FontAwesome icons correctly', () => {
-      render(<RepositoriesCard repositories={[mockRepository]} />)
-      
-      // Check for chevron icons in show more/less button
       const repositories = createMockRepositories(5)
       render(<RepositoriesCard repositories={repositories} />)
       
       const showMoreButton = screen.getByRole('button', { name: /show more/i })
       expect(showMoreButton.querySelector('[data-testid="icon-chevron-down"]')).toBeInTheDocument()
📝 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.

Suggested change
expect(truncatedText).toHaveTextContent('Test Repository')
})
it('renders FontAwesome icons correctly', () => {
render(<RepositoriesCard repositories={[mockRepository]} />)
// Check for chevron icons in show more/less button
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)
const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(showMoreButton.querySelector('[data-testid="icon-chevron-down"]')).toBeInTheDocument()
fireEvent.click(showMoreButton)
it('renders FontAwesome icons correctly', () => {
const repositories = createMockRepositories(5)
render(<RepositoriesCard repositories={repositories} />)
const showMoreButton = screen.getByRole('button', { name: /show more/i })
expect(
showMoreButton.querySelector('[data-testid="icon-chevron-down"]')
).toBeInTheDocument()
fireEvent.click(showMoreButton)
// ...rest of the test
})
🤖 Prompt for AI Agents
In frontend/__tests__/unit/components/RepositoriesCard.test.tsx around lines 493
to 507, the test renders the RepositoriesCard component twice within the same
test case, which can cause unpredictable behavior. To fix this, remove the first
render call with mockRepository and only render once with the intended
repositories array. Ensure all assertions and interactions are done after this
single render to maintain test consistency.

Comment on lines +344 to +370
async function newFunction({
headers,
stats,
}: {
headers: string[]
stats: {
activeChaptersStats: number
activeProjectsStats: number
countriesStats: number
contributorsStats: number
slackWorkspaceStats: number
}
}): Promise<void> {
// Wait for headers
for (const header of headers) {
await waitFor(() => {
expect(screen.getByText(header)).toBeInTheDocument()
})
}

// Wait for stats (millified + "+")
for (const value of Object.values(stats)) {
await waitFor(function () {
expect(screen.getByText(`${millify(value)}+`)).toBeInTheDocument()
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use a more descriptive function name

The function name newFunction is not descriptive. Consider renaming it to something that better describes its purpose, such as waitForStatsToRender or verifyStatsDisplay.

-async function newFunction({
+async function waitForStatsToRender({
   headers,
   stats,
 }: {

Also update the call site at line 273:

-    await newFunction({ headers, stats })
+    await waitForStatsToRender({ headers, stats })
📝 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.

Suggested change
async function newFunction({
headers,
stats,
}: {
headers: string[]
stats: {
activeChaptersStats: number
activeProjectsStats: number
countriesStats: number
contributorsStats: number
slackWorkspaceStats: number
}
}): Promise<void> {
// Wait for headers
for (const header of headers) {
await waitFor(() => {
expect(screen.getByText(header)).toBeInTheDocument()
})
}
// Wait for stats (millified + "+")
for (const value of Object.values(stats)) {
await waitFor(function () {
expect(screen.getByText(`${millify(value)}+`)).toBeInTheDocument()
})
}
}
async function waitForStatsToRender({
headers,
stats,
}: {
headers: string[]
stats: {
activeChaptersStats: number
activeProjectsStats: number
countriesStats: number
contributorsStats: number
slackWorkspaceStats: number
}
}): Promise<void> {
// Wait for headers
for (const header of headers) {
await waitFor(() => {
expect(screen.getByText(header)).toBeInTheDocument()
})
}
// Wait for stats (millified + "+")
for (const value of Object.values(stats)) {
await waitFor(function () {
expect(screen.getByText(`${millify(value)}+`)).toBeInTheDocument()
})
}
}
🤖 Prompt for AI Agents
In frontend/__tests__/unit/pages/Home.test.tsx around lines 344 to 370, rename
the function from 'newFunction' to a more descriptive name like
'waitForStatsToRender' or 'verifyStatsDisplay' to clearly convey its purpose.
Also, update the function call at line 273 to use the new function name to
maintain consistency and avoid errors.

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: 2

♻️ Duplicate comments (3)
frontend/__tests__/unit/components/ActionButton.test.tsx (2)

1-557: File incorrectly repurposed - ActionButton tests missing

This file has been completely overwritten with RepositoriesCard tests, removing the original ActionButton tests. Since the ActionButton component still exists and is used by other components, this creates a gap in test coverage.

To fix this issue:

  1. Move these tests to RepositoriesCard.test.tsx or merge with the existing tests there
  2. Delete or restore this file to contain proper ActionButton tests
  3. Create appropriate tests for the ActionButton component

507-521: Duplicate render calls in test

The test renders the component twice - first with mockRepository and then with repositories, which can cause unpredictable behavior.

     it('renders FontAwesome icons correctly', () => {
-      render(<RepositoriesCard repositories={[mockRepository]} />)
-
-      // Check for chevron icons in show more/less button
       const repositories = createMockRepositories(5)
       render(<RepositoriesCard repositories={repositories} />)

       const showMoreButton = screen.getByRole('button', { name: /show more/i })
frontend/__tests__/unit/components/RepositoriesCard.test.tsx (1)

511-525: Remove duplicate render call

This test renders the component twice, which could cause unpredictable behavior.

     it('renders FontAwesome icons correctly', () => {
-      render(<RepositoriesCard repositories={[mockRepository]} />)
-
-      // Check for chevron icons in show more/less button
       const repositories = createMockRepositories(5)
       render(<RepositoriesCard repositories={repositories} />)

       const showMoreButton = screen.getByRole('button', { name: /show more/i })
🧹 Nitpick comments (2)
frontend/fix-imports.js (2)

15-66: Improve script flexibility and error handling.

The current implementation has several areas for improvement:

  1. Hardcoded file list: Consider making the file list configurable or auto-discoverable
  2. Sync file operations: Use async operations to avoid blocking
  3. Missing error handling: File operations can fail
+const { promisify } = require('util');
+const readFile = promisify(fs.readFile);
+const writeFile = promisify(fs.writeFile);

-files.forEach(file => {
+const processFile = async (file) => {
+  try {
     const fullPath = file;
-    if (fs.existsSync(fullPath)) {
+    if (await fs.promises.access(fullPath).then(() => true).catch(() => false)) {
       console.log(`Processing ${file}...`);
-      const content = fs.readFileSync(fullPath, 'utf8');
+      const content = await readFile(fullPath, 'utf8');
       // ... rest of processing logic
-      fs.writeFileSync(fullPath, newContent);
+      await writeFile(fullPath, newContent);
       console.log(`Fixed imports in ${file}`);
     } else {
       console.log(`File not found: ${file}`);
     }
+  } catch (error) {
+    console.error(`Error processing ${file}:`, error.message);
+  }
-});
+};

+const main = async () => {
+  await Promise.all(files.map(processFile));
+  console.log('Import reordering complete!');
+};
+
+main().catch(console.error);

32-48: Enhance import detection logic.

The current import detection could be more robust and handle edge cases better.

     lines.forEach((line, index) => {
-      if (line.trim().startsWith('import ')) {
+      const trimmedLine = line.trim();
+      if (trimmedLine.startsWith('import ') && !trimmedLine.startsWith('//')) {
-        if (line.includes('from \'react\'') || line.includes('from "react"')) {
+        if (/from ['"]react['"]/.test(line)) {
           reactImports.push(line);
-        } else if (line.includes('import type ') || line.includes('import { type ')) {
+        } else if (/^import\s+type\s+|import\s*{\s*type\s+/.test(trimmedLine)) {
           typeImports.push(line);
-        } else if (line.includes('wrappers/testUtil')) {
+        } else if (/['"]wrappers\/testUtil['"]/.test(line)) {
           utilImports.push(line);
         } else {
           otherImports.push(line);
         }
-      } else if (line.trim() === '' && inImportSection) {
+      } else if (trimmedLine === '' && inImportSection) {
         // Continue - empty lines in import section
       } else {
         inImportSection = false;
         nonImportLines.push(line);
       }
     });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d292c8f and e0c340c.

📒 Files selected for processing (60)
  • .coderabbit.yaml (1 hunks)
  • .pre-commit-config.yaml (1 hunks)
  • frontend/__tests__/unit/components/ActionButton.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/AutoScrollToTop.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/BreadCrumbs.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/Card.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/CardDetailsPage.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/ChapterMap.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/ContributorAvatar.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/Footer.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/HealthMetrics.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/InfoBlock.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/InfoItem.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/LoadingSpinner.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/LoginPageContent.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/MetricsCard.test.tsx (2 hunks)
  • frontend/__tests__/unit/components/MetricsScoreCircle.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/Modal.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/ModeToggle.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/MultiSearch.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/NavButton.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/NavDropDown.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/Pagination.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/ProjectsDashboardNavBar.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/ScrollToTop.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/SearchPageLayout.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/SortBy.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/SponsorCard.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/ToggleableList.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/TopContributorsList.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/UserCard.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/UserMenu.test.tsx (1 hunks)
  • frontend/__tests__/unit/global-error.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/About.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/ChapterDetails.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Chapters.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Committees.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Contribute.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Header.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Home.test.tsx (4 hunks)
  • frontend/__tests__/unit/pages/Login.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Organization.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/OrganizationDetails.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/ProjectDetails.test.tsx (2 hunks)
  • frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Projects.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/ProjectsHealthDashboardOverview.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/RepositoryDetails.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/SnapshotDetails.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Snapshots.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/UserDetails.test.tsx (1 hunks)
  • frontend/__tests__/unit/pages/Users.test.tsx (1 hunks)
  • frontend/fix-imports.js (1 hunks)
  • frontend/jest.setup.ts (0 hunks)
  • frontend/src/wrappers/testUtil.tsx (2 hunks)
  • frontend/tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/jest.setup.ts
✅ Files skipped from review due to trivial changes (37)
  • .coderabbit.yaml
  • frontend/tests/unit/components/BreadCrumbs.test.tsx
  • frontend/tests/unit/components/SearchPageLayout.test.tsx
  • frontend/tests/unit/pages/ProjectsHealthDashboardOverview.test.tsx
  • frontend/tests/unit/components/Modal.test.tsx
  • frontend/tests/unit/pages/Login.test.tsx
  • frontend/tests/unit/components/MetricsScoreCircle.test.tsx
  • frontend/tests/unit/components/ChapterMap.test.tsx
  • frontend/tests/unit/global-error.test.tsx
  • frontend/tests/unit/components/ToggleableList.test.tsx
  • frontend/tests/unit/pages/Organization.test.tsx
  • frontend/tests/unit/pages/ApiKeysPage.test.tsx
  • frontend/tests/unit/components/Pagination.test.tsx
  • frontend/tests/unit/components/TopContributorsList.test.tsx
  • frontend/tests/unit/pages/Snapshots.test.tsx
  • frontend/tests/unit/pages/ChapterDetails.test.tsx
  • frontend/tests/unit/components/SponsorCard.test.tsx
  • frontend/tsconfig.json
  • frontend/tests/unit/components/UserMenu.test.tsx
  • frontend/tests/unit/components/Footer.test.tsx
  • frontend/tests/unit/components/CardDetailsPage.test.tsx
  • frontend/tests/unit/pages/Users.test.tsx
  • frontend/tests/unit/components/AutoScrollToTop.test.tsx
  • frontend/tests/unit/components/ContributorAvatar.test.tsx
  • frontend/tests/unit/components/ScrollToTop.test.tsx
  • frontend/tests/unit/components/ProjectsDashboardNavBar.test.tsx
  • frontend/tests/unit/components/InfoItem.test.tsx
  • frontend/tests/unit/components/SortBy.test.tsx
  • frontend/tests/unit/components/LoadingSpinner.test.tsx
  • frontend/tests/unit/components/Card.test.tsx
  • frontend/tests/unit/components/HealthMetrics.test.tsx
  • frontend/tests/unit/components/LoginPageContent.test.tsx
  • frontend/tests/unit/pages/Committees.test.tsx
  • frontend/tests/unit/pages/UserDetails.test.tsx
  • frontend/tests/unit/components/ModeToggle.test.tsx
  • frontend/tests/unit/pages/Header.test.tsx
  • frontend/tests/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/tests/unit/pages/ProjectDetails.test.tsx
  • frontend/tests/unit/components/MetricsCard.test.tsx
  • frontend/tests/unit/pages/Home.test.tsx
  • frontend/src/wrappers/testUtil.tsx
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: Rajgupta36
PR: OWASP/Nest#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.
📚 Learning: when testing react page components that use mocked form components, validation logic should be teste...
Learnt from: Rajgupta36
PR: OWASP/Nest#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/pages/OrganizationDetails.test.tsx
  • frontend/__tests__/unit/pages/SnapshotDetails.test.tsx
  • frontend/__tests__/unit/pages/Contribute.test.tsx
  • frontend/__tests__/unit/pages/Projects.test.tsx
  • frontend/__tests__/unit/components/InfoBlock.test.tsx
  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
  • frontend/__tests__/unit/components/UserCard.test.tsx
  • frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
  • frontend/__tests__/unit/pages/About.test.tsx
  • frontend/__tests__/unit/components/NavButton.test.tsx
  • frontend/__tests__/unit/components/NavDropDown.test.tsx
  • frontend/__tests__/unit/pages/Chapters.test.tsx
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx
  • frontend/__tests__/unit/pages/CommitteeDetails.test.tsx
  • frontend/__tests__/unit/components/MultiSearch.test.tsx
  • frontend/__tests__/unit/components/ActionButton.test.tsx
📚 Learning: use react's useid() hook rather than manually generating random strings when creating accessibility ...
Learnt from: codic-yeeshu
PR: OWASP/Nest#1444
File: frontend/src/components/NavDropDown.tsx:0-0
Timestamp: 2025-04-30T13:41:20.846Z
Learning: Use React's useId() hook rather than manually generating random strings when creating accessibility identifiers for UI components. This creates stable, unique IDs without causing hydration mismatches.

Applied to files:

  • frontend/__tests__/unit/components/InfoBlock.test.tsx
  • frontend/__tests__/unit/components/NavDropDown.test.tsx
📚 Learning: in the owasp nest project's barchart component (frontend/src/components/barchart.tsx), the days and ...
Learnt from: ahmedxgouda
PR: OWASP/Nest#1703
File: frontend/src/components/BarChart.tsx:33-46
Timestamp: 2025-07-03T03:08:03.290Z
Learning: In the OWASP Nest project's BarChart component (frontend/src/components/BarChart.tsx), the days and requirements arrays are guaranteed to always have the same length in their use cases, so input validation for array length matching is not needed.

Applied to files:

  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
  • frontend/__tests__/unit/components/ActionButton.test.tsx
📚 Learning: in the detailscard component (frontend/src/components/carddetailspage.tsx), there's a length check b...
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx
📚 Learning: in the detailscard component (frontend/src/components/carddetailspage.tsx), there's a safety check t...
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
📚 Learning: the owasp/nest project uses mypy for static type checking, configured in .pre-commit-config.yaml wit...
Learnt from: yashgoyal0110
PR: OWASP/Nest#0
File: :0-0
Timestamp: 2025-04-06T06:57:42.144Z
Learning: The OWASP/Nest project uses mypy for static type checking, configured in .pre-commit-config.yaml with settings to exclude tests and use backend/pyproject.toml as the config file.

Applied to files:

  • .pre-commit-config.yaml
📚 Learning: in next.js 13+ app router, components with the 'use client' directive run entirely on the client sid...
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/__tests__/unit/components/NavDropDown.test.tsx
  • frontend/__tests__/unit/pages/Chapters.test.tsx
  • frontend/__tests__/unit/components/MultiSearch.test.tsx
🧬 Code Graph Analysis (1)
frontend/__tests__/unit/components/ActionButton.test.tsx (2)
frontend/src/types/project.ts (1)
  • RepositoryCardProps (52-62)
frontend/src/wrappers/testUtil.tsx (3)
  • render (13-13)
  • screen (13-13)
  • fireEvent (13-13)
🔇 Additional comments (17)
frontend/__tests__/unit/pages/OrganizationDetails.test.tsx (1)

4-4: LGTM: Import standardization.

The change to import testing utilities from the centralized wrappers/testUtil module aligns with the project's standardization effort. This improves consistency across the test suite.

frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (1)

5-5: LGTM: Consistent import standardization.

The import change maintains consistency with the project's effort to centralize testing utilities through wrappers/testUtil.

frontend/__tests__/unit/pages/Projects.test.tsx (1)

4-4: LGTM: Import standardization completed.

The import change to use wrappers/testUtil completes the standardization effort across the test files in this PR.

frontend/__tests__/unit/pages/SnapshotDetails.test.tsx (1)

6-6: LGTM! Import centralization improves consistency.

This change aligns all testing utilities to import from the centralized wrappers/testUtil module, which is a good practice for standardizing test setup across the codebase.

frontend/__tests__/unit/components/MultiSearch.test.tsx (1)

5-5: LGTM! Testing utilities centralized appropriately.

The import change to use wrappers/testUtil maintains consistency with the codebase-wide refactoring to centralize testing utilities.

frontend/__tests__/unit/components/InfoBlock.test.tsx (1)

7-7: LGTM! Consistent import centralization.

The change to import testing utilities from wrappers/testUtil aligns with the systematic refactoring to centralize testing utilities across the codebase.

frontend/__tests__/unit/pages/Contribute.test.tsx (1)

4-4: LGTM! Completes import centralization for this file.

This change aligns the remaining testing utilities with the existing render import from wrappers/testUtil, completing the centralization pattern for this file.

frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (1)

4-4: LGTM! Efficient consolidation of testing utilities.

This change effectively centralizes all testing utilities (render, screen, waitFor, fireEvent) to import from wrappers/testUtil in a single statement, completing the systematic refactoring.

frontend/__tests__/unit/pages/Chapters.test.tsx (1)

4-4: LGTM! Good refactoring to centralize testing utilities.

This change standardizes the import of testing utilities through the wrappers/testUtil module, which improves maintainability and allows for consistent test environment configuration across the codebase.

frontend/__tests__/unit/components/NavDropDown.test.tsx (1)

3-3: LGTM! Consistent with the testing utilities consolidation effort.

This change aligns with the broader refactoring to standardize testing utility imports through wrappers/testUtil, maintaining consistency across the test suite.

frontend/__tests__/unit/pages/About.test.tsx (1)

8-8: LGTM! Continues the testing utilities standardization.

This change consolidates multiple testing utilities (fireEvent, screen, waitFor, within) under the centralized wrappers/testUtil import, maintaining consistency with the broader refactoring effort.

frontend/__tests__/unit/pages/RepositoryDetails.test.tsx (1)

6-6: LGTM! Consistent testing utilities refactoring.

This change standardizes the import of testing utilities including act, maintaining consistency with the broader effort to centralize testing utility imports through wrappers/testUtil.

frontend/__tests__/unit/components/UserCard.test.tsx (1)

2-2: LGTM! Completes the testing utilities consolidation.

This change standardizes the import of testing utilities (render, screen, fireEvent, cleanup) through wrappers/testUtil, completing the consistent refactoring pattern applied across all test files in this PR.

frontend/__tests__/unit/components/NavButton.test.tsx (2)

3-5: Import standardization looks good

The migration to import testing utilities from wrappers/testUtil aligns with the project-wide standardization effort.


8-12: JSDoc positioning improved

Moving the file-level JSDoc comment below imports follows better documentation practices.

frontend/__tests__/unit/components/RepositoriesCard.test.tsx (2)

1-2: Import standardization implemented correctly

The migration to import testing utilities from wrappers/testUtil is consistent with the project-wide standardization.


12-46: Excellent test coverage and organization

The test suite provides comprehensive coverage with:

  • Well-structured mocks for dependencies
  • Detailed test cases covering all scenarios mentioned in the PR objectives
  • Proper organization into descriptive test blocks
  • Good handling of edge cases and accessibility testing

Also applies to: 59-93, 95-557

Comment on lines +29 to +39
# Temporarily disabled due to Windows path issue
# - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt
# rev: 0.2.3
# hooks:
# - id: yamlfmt
# args:
# - --implicit_start
# - --mapping=2
# - --offset=2
# - --sequence=4
# exclude: (.github|pnpm-lock.yaml)
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Track the Windows path issue and consider alternative solutions.

While temporarily disabling the yamlfmt hook addresses the immediate Windows compatibility issue, this could lead to inconsistent YAML formatting across the codebase. Consider documenting this as a known issue and exploring alternative solutions such as:

  1. Using a different yamlfmt hook that's Windows-compatible
  2. Adding platform-specific conditions to the pre-commit configuration
  3. Setting up a timeline to resolve the underlying path issue

🏁 Script executed:

#!/bin/bash
# Check if there are alternative yamlfmt hooks available
echo "Searching for alternative yamlfmt pre-commit hooks..."
curl -s "https://api.github.com/search/repositories?q=yamlfmt+pre-commit&type=repositories" | jq -r '.items[] | select(.stargazers_count > 10) | "\(.full_name) - \(.description)"' | head -5

Length of output: 388


Document the Windows path issue and define a plan to restore consistent YAML formatting

Temporarily disabling the yamlfmt hook works around the Windows path problem but risks divergent YAML styles. Since no widely adopted alternative pre-commit hook for yamlfmt was found, please:

  • File an upstream issue against jumanjihouse/pre-commit-hook-yamlfmt to add proper Windows‐path support.
  • Guard the existing hook so it only runs in CI (Linux/macOS) environments. For example, wrap it in a local repo entry or use an environment‐variable check in your CI configuration.
  • Update CONTRIBUTING.md (or your developer guide) with instructions for Windows users on running yamlfmt manually (e.g. via pip install yamlfmt).
  • Set a concrete timeline (e.g. 2 weeks) to revisit this workaround, merge upstream fixes, and re-enable the hook across all platforms.
🤖 Prompt for AI Agents
In .pre-commit-config.yaml around lines 29 to 39, the yamlfmt hook is disabled
due to a Windows path issue. To fix this, file an issue upstream with
jumanjihouse/pre-commit-hook-yamlfmt requesting Windows path support, modify the
config to run the yamlfmt hook only in CI environments (Linux/macOS) by adding
environment checks or wrapping it in a local repo entry, update CONTRIBUTING.md
with instructions for Windows users to run yamlfmt manually, and set a timeline
(e.g., 2 weeks) to revisit and fully re-enable the hook after upstream fixes.

Comment on lines +1 to +3
const fs = require('fs');
const glob = require('glob');

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused import and add error handling.

The glob package is imported but never used in the script.

 const fs = require('fs');
-const glob = require('glob');

Additionally, consider adding error handling for file operations to make the script more robust.

📝 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.

Suggested change
const fs = require('fs');
const glob = require('glob');
const fs = require('fs');
🤖 Prompt for AI Agents
In frontend/fix-imports.js at lines 1 to 3, remove the unused import of the
'glob' package since it is not used anywhere in the script. Additionally, add
error handling around file system operations such as reading or writing files
using 'fs' to catch and handle potential errors gracefully, improving the
robustness of the script.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Aug 6, 2025

Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

This PR contains a lot of unrelated changes and doesn't seem to follow our contributing guidelines.

@arkid15r
Copy link
Collaborator

arkid15r commented Aug 6, 2025

The issue was resolved in other PR -- sorry for the confusion.

@arkid15r arkid15r closed this Aug 6, 2025
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.

Add tests for <RepositoriesCard> component

2 participants