Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1e0cd00
fix: add contrast improvement styles to global error component
sameersharmadev Dec 11, 2025
17140aa
Address make-check issues
kasya Dec 12, 2025
8992f06
Merge branch 'OWASP:main' into main
sameersharmadev Dec 14, 2025
e5125ac
migrate to react-icons
sameersharmadev Dec 10, 2025
6656fb3
apply code quality suggestions
sameersharmadev Dec 13, 2025
69196c8
update unit tests
sameersharmadev Dec 14, 2025
6af9f8e
implement coderabbit suggestions
sameersharmadev Dec 14, 2025
ccd0c23
chore: update pnpm-lock.yaml, fix NavButton test, migrate ChapterMap …
sameersharmadev Dec 14, 2025
071ca64
update frontend/src/components/ChapterMap
sameersharmadev Dec 14, 2025
58af16d
restore calendarbutton logic lost during bad merge
sameersharmadev Dec 15, 2025
a09f121
address quality gate issues
sameersharmadev Dec 15, 2025
0271429
chore: yet another code quality gate fix
sameersharmadev Dec 15, 2025
847444c
chore: address coderabbit review comments
sameersharmadev Dec 15, 2025
2e9de32
address make check issues
sameersharmadev Dec 16, 2025
e6c8d67
Revert accidental modification to cpell/Makefile
sameersharmadev Dec 16, 2025
92ebcd4
address final coderabbit and sonarqube reviews
sameersharmadev Dec 16, 2025
5030680
fix typescript definition for optional icon prop
sameersharmadev Dec 16, 2025
e8ca5aa
Update icons style and size
kasya Dec 17, 2025
c3336ee
Merge branch 'main' of github.com:OWASP/Nest into pr/sameersharmadev/…
kasya Dec 17, 2025
c93779c
Update code
arkid15r Dec 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions frontend/__tests__/unit/components/AnchorTitle.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { library } from '@fortawesome/fontawesome-svg-core'
import { faLink } from '@fortawesome/free-solid-svg-icons'
import { screen, render, fireEvent, waitFor } from '@testing-library/react'
import slugifyMock from 'utils/slugify'
import AnchorTitle from 'components/AnchorTitle'

library.add(faLink)

jest.mock('utils/slugify', () => ({
__esModule: true,
default: jest.fn((str: string) =>
Expand Down Expand Up @@ -101,7 +97,7 @@ describe('AnchorTitle Component', () => {
expect(titleElement).toHaveAttribute('data-anchor-title', 'true')
})

it('renders FontAwesome link icon', () => {
it('renders react-icons link icon', () => {
render(<AnchorTitle title="Test" />)
const link = screen.getByRole('link')
const icon = link.querySelector('svg')
Expand Down
39 changes: 17 additions & 22 deletions frontend/__tests__/unit/components/Badges.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,26 @@ import { render, screen } from '@testing-library/react'
import React from 'react'
import Badges from 'components/Badges'

jest.mock('wrappers/FontAwesomeIconWrapper', () => {
const RealWrapper = jest.requireActual('wrappers/FontAwesomeIconWrapper').default

const getName = (icon) => {
if (!icon) return 'medal'
if (typeof icon === 'string') {
const m = icon.match(/fa-([a-z0-9-]+)$/i)
if (m) return m[1]
const last = icon.trim().split(/\s+/).pop() || ''
return last.replace(/^fa-/, '') || 'medal'
}
if (Array.isArray(icon) && icon.length >= 2) return String(icon[1])
if (icon && typeof icon === 'object') return icon.iconName || String(icon[1] ?? 'medal')
return 'medal'
}

return function MockFontAwesomeIconWrapper(props) {
const name = getName(props.icon)
jest.mock('wrappers/IconWrapper', () => ({
IconWrapper: ({
icon,
className,
...props
}: {
icon: React.ComponentType<{ className?: string }>
className?: string
}) => {
let iconName = icon.name?.toLowerCase() || 'medal'
iconName = iconName.replace(/^fa/, '')
iconName = iconName.replace(/^reg/, '')
if (!iconName) iconName = 'medal'
return (
<div data-testid={`icon-${name}`}>
<RealWrapper {...props} />
<div data-testid="badge-icon" data-icon={iconName} className={className} {...props}>
<svg />
</div>
)
}
})
},
}))

jest.mock('@heroui/tooltip', () => ({
Tooltip: ({
Expand Down
11 changes: 3 additions & 8 deletions frontend/__tests__/unit/components/BarChart.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { library } from '@fortawesome/fontawesome-svg-core'
import { faFire } from '@fortawesome/free-solid-svg-icons'
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom'
import React from 'react'

// Register FontAwesome icon
library.add(faFire)
import { FaFire } from 'react-icons/fa'
import '@testing-library/jest-dom'

// Mock react-apexcharts completely
jest.mock('react-apexcharts', () => {
Expand Down Expand Up @@ -170,8 +166,7 @@ describe('<BarChart />', () => {
})

it('renders with custom icon when provided', () => {
// cspell:ignore fas
renderWithTheme(<BarChart {...mockProps} icon={['fas', 'fire']} />)
renderWithTheme(<BarChart {...mockProps} icon={FaFire} />)
expect(screen.getByTestId('anchor-title')).toHaveTextContent('Calories Burned')
expect(screen.getByTestId('card-icon')).toBeInTheDocument()
})
Expand Down
24 changes: 6 additions & 18 deletions frontend/__tests__/unit/components/CalendarButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { faCalendarDay, faCalendarPlus } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { render, screen } from '@testing-library/react'
import { FaCalendarDay, FaCalendarPlus } from 'react-icons/fa6'
import CalendarButton from 'components/CalendarButton'

const mockEvent = {
Expand All @@ -25,18 +24,15 @@ describe('CalendarButton', () => {
expect(link.tagName).toBe('A')
})

it('renders default FontAwesome calendar-plus icon', () => {
it('renders default calendar-plus icon', () => {
render(<CalendarButton event={mockEvent} />)
const svg = document.querySelector('svg')
expect(svg).toBeInTheDocument()
})

it('renders custom icon when provided', () => {
render(
<CalendarButton
event={mockEvent}
icon={<FontAwesomeIcon icon={faCalendarDay} data-testid="custom-icon" />}
/>
<CalendarButton event={mockEvent} icon={<FaCalendarDay data-testid="custom-icon" />} />
)
expect(screen.getByTestId('custom-icon')).toBeInTheDocument()
})
Expand Down Expand Up @@ -185,12 +181,9 @@ describe('CalendarButton', () => {
})

describe('icon prop extensibility', () => {
it('accepts FontAwesome icon as JSX', () => {
it('accepts icon as JSX', () => {
render(
<CalendarButton
event={mockEvent}
icon={<FontAwesomeIcon icon={faCalendarPlus} className="custom-icon-class" />}
/>
<CalendarButton event={mockEvent} icon={<FaCalendarPlus className="custom-icon-class" />} />
)
const svg = document.querySelector('svg')
expect(svg).toHaveClass('custom-icon-class')
Expand Down Expand Up @@ -321,12 +314,7 @@ describe('CalendarButton', () => {
render(
<CalendarButton
event={mockEvent}
icon={
<FontAwesomeIcon
icon={faCalendarPlus}
className="h-6 w-6 text-blue-500 hover:text-blue-700"
/>
}
icon={<FaCalendarPlus className="h-6 w-6 text-blue-500 hover:text-blue-700" />}
/>
)
const svg = document.querySelector('svg')
Expand Down
61 changes: 30 additions & 31 deletions frontend/__tests__/unit/components/Card.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { screen, render, fireEvent } from '@testing-library/react'
import { ReactNode } from 'react'
import React, { ReactNode } from 'react'
import { FaLeaf, FaFire, FaCrown, FaStar, FaGithub, FaTwitter } from 'react-icons/fa6'
import type { CardProps } from 'types/card'
import Card from 'components/Card'

Expand All @@ -12,11 +13,6 @@ interface MockLinkProps {
className?: string
}

interface MockFontAwesomeIconProps {
icon: unknown
className?: string
}

interface MockTooltipProps {
children: ReactNode
content: string
Expand Down Expand Up @@ -63,14 +59,6 @@ jest.mock('next/link', () => {
}
})

jest.mock('@fortawesome/react-fontawesome', () => ({
FontAwesomeIcon: ({ icon, className }: MockFontAwesomeIconProps) => (
<span data-testid="font-awesome-icon" className={className}>
{String(icon)}
</span>
),
}))

jest.mock('@heroui/tooltip', () => ({
Tooltip: ({ children, content, id }: MockTooltipProps) => (
<div data-testid="tooltip" title={content} id={id}>
Expand All @@ -79,15 +67,22 @@ jest.mock('@heroui/tooltip', () => ({
),
}))

jest.mock('wrappers/FontAwesomeIconWrapper', () => {
return function FontAwesomeIconWrapper({ icon, className }: MockFontAwesomeIconProps) {
jest.mock('wrappers/IconWrapper', () => ({
IconWrapper: ({
icon,
className,
}: {
icon: React.ComponentType<{ className?: string }>
className?: string
}) => {
const iconName = icon?.name?.toLowerCase().replace('fa', '') || 'icon'
return (
<span data-testid="font-awesome-wrapper" className={className}>
{String(icon)}
<span data-testid="icon-wrapper" data-icon={iconName} className={className}>
<svg />
</span>
)
}
})
},
}))

jest.mock('components/ActionButton', () => {
return function ActionButton({ children, onClick, tooltipLabel, url }: MockActionButtonProps) {
Expand Down Expand Up @@ -131,7 +126,11 @@ jest.mock('components/MarkdownWrapper', () => {
})

jest.mock('utils/urlIconMappings', () => ({
getSocialIcon: jest.fn().mockReturnValue('mocked-social-icon'),
getSocialIcon: jest.fn().mockReturnValue(({ className }: { className?: string }) => (
<span data-testid="social-icon" className={className}>
<svg />
</span>
)),
}))

jest.mock('utils/data', () => ({
Expand Down Expand Up @@ -179,12 +178,12 @@ describe('Card', () => {
it('conditionally renders level badge when provided', () => {
const propsWithLevel = {
...baseProps,
level: { level: 'Beginner', color: '#4CAF50', icon: 'leaf-icon' },
level: { level: 'Beginner', color: '#4CAF50', icon: FaLeaf },
}

render(<Card {...propsWithLevel} />)
expect(screen.getByTestId('tooltip')).toBeInTheDocument()
expect(screen.getByTestId('font-awesome-wrapper')).toBeInTheDocument()
expect(screen.getByTestId('icon-wrapper')).toBeInTheDocument()
})

it('does not render level badge when not provided', () => {
Expand Down Expand Up @@ -229,12 +228,12 @@ describe('Card', () => {
const propsWithSocial = {
...baseProps,
social: [
{ title: 'GitHub', url: 'https://github.com/test', icon: 'github' },
{ title: 'Twitter', url: 'https://twitter.com/test', icon: 'twitter' },
{ title: 'GitHub', url: 'https://github.com/test', icon: FaGithub },
{ title: 'Twitter', url: 'https://twitter.com/test', icon: FaTwitter },
],
}
render(<Card {...propsWithSocial} />)
expect(screen.getAllByTestId('font-awesome-icon')).toHaveLength(2)
expect(screen.getAllByTestId('social-icon')).toHaveLength(2)

const allLinks = screen.getAllByRole('link')
expect(allLinks.length).toBeGreaterThan(1)
Expand Down Expand Up @@ -296,7 +295,7 @@ describe('Card', () => {
it('applies different level badge colors based on props', () => {
const propsWithLevel = {
...baseProps,
level: { level: 'Advanced', color: '#FF5722', icon: 'fire-icon' },
level: { level: 'Advanced', color: '#FF5722', icon: FaFire },
}

render(<Card {...propsWithLevel} />)
Expand Down Expand Up @@ -400,7 +399,7 @@ describe('Card', () => {
it('has proper accessibility attributes', () => {
const propsWithTooltip = {
...baseProps,
level: { level: 'Intermediate', color: '#2196F3', icon: 'star-icon' },
level: { level: 'Intermediate', color: '#2196F3', icon: FaStar },
tooltipLabel: 'Click to contribute',
}

Expand Down Expand Up @@ -466,11 +465,11 @@ describe('Card', () => {
it('renders complete card with all optional props', () => {
const fullProps = {
...baseProps,
level: { level: 'Expert', color: '#9C27B0', icon: 'crown-icon' },
level: { level: 'Expert', color: '#9C27B0', icon: FaCrown },
icons: { react: 'active', typescript: 'active' },
projectName: 'Full Stack Project',
projectLink: 'https://fullstack.com',
social: [{ title: 'GitHub', url: 'https://github.com/full', icon: 'active' }],
social: [{ title: 'GitHub', url: 'https://github.com/full', icon: FaGithub }],
topContributors: [
{
login: 'expert',
Expand All @@ -486,7 +485,7 @@ describe('Card', () => {
expect(screen.getByTestId('tooltip')).toBeInTheDocument()
expect(screen.getAllByTestId('display-icon')).toHaveLength(2)
expect(screen.getByRole('link', { name: 'Full Stack Project' })).toBeInTheDocument()
expect(screen.getByTestId('font-awesome-icon')).toBeInTheDocument()
expect(screen.getByTestId('social-icon')).toBeInTheDocument()
expect(screen.getByTestId('contributor-avatar')).toBeInTheDocument()
})
})
Loading