Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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: 3 additions & 3 deletions frontend/__tests__/unit/components/AnchorTitle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,13 @@ describe('AnchorTitle Component', () => {

const titleRegex = /^#[a-z0-9-]+$/

titles.forEach((title) => {
const { unmount } = render(<AnchorTitle title={title} />)
for (const title of titles) {
const { unmount } = render(<AnchorTitle title={title} />)
const link = screen.getByRole('link')
const href = link.getAttribute('href')
expect(href).toMatch(titleRegex)
unmount()
})
}
})
})

Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/unit/components/Badges.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,16 @@ describe('Badges Component', () => {
{ cssClass: 'bug_slash', expectedIcon: 'bug' }, // Backend snake_case input
]

backendIcons.forEach(({ cssClass, expectedIcon }) => {
it(`renders ${cssClass} icon correctly (transforms snake_case to camelCase)`, () => {
render(<Badges name={`${cssClass} Badge`} cssClass={cssClass} />)
for (const backendIcon of backendIcons) {
it(`renders ${backendIcon.cssClass} icon correctly (transforms snake_case to camelCase)`, () => {
render(<Badges name={`${backendIcon.cssClass} Badge`} cssClass={backendIcon.cssClass} />)

const icon = screen.getByTestId('badge-icon')
expect(icon).toBeInTheDocument()
expect(icon).toHaveAttribute('data-icon', expectedIcon)
expect(icon).toHaveAttribute('data-icon', backendIcon.expectedIcon)
})
})

}
it('handles camelCase input directly', () => {
render(<Badges name="Bug Slash Badge" cssClass="bugSlash" />)

Expand Down
37 changes: 11 additions & 26 deletions frontend/__tests__/unit/components/BarChart.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,32 +234,17 @@ describe('<BarChart />', () => {
expect(series[0].name).toBe('Actual')
expect(series[0].data).toHaveLength(3)

series[0].data.forEach(
(
dataPoint: {
x: string
y: number
goals: Array<{
name: string
value: number
strokeWidth: number
strokeHeight: number
strokeLineCap: string
strokeColor: string
}>
},
index: number
) => {
expect(dataPoint.x).toBe(mockProps.labels[index])
expect(dataPoint.y).toBe(mockProps.days[index])
expect(dataPoint.goals).toHaveLength(1)
expect(dataPoint.goals[0].name).toBe('Requirement')
expect(dataPoint.goals[0].value).toBe(mockProps.requirements[index])
expect(dataPoint.goals[0].strokeWidth).toBe(5)
expect(dataPoint.goals[0].strokeHeight).toBe(15)
expect(dataPoint.goals[0].strokeLineCap).toBe('round')
}
)
for (const [index, dataPoint] of series[0].data.entries()) {
expect(dataPoint.x).toBe(mockProps.labels[index])
expect(dataPoint.y).toBe(mockProps.days[index])
expect(dataPoint.goals).toHaveLength(1)
expect(dataPoint.goals[0].name).toBe('Requirement')
expect(dataPoint.goals[0].value).toBe(mockProps.requirements[index])
expect(dataPoint.goals[0].strokeWidth).toBe(5)
expect(dataPoint.goals[0].strokeHeight).toBe(15)
expect(dataPoint.goals[0].strokeLineCap).toBe('round')
}

})

it('configures colors array correctly', () => {
Expand Down
8 changes: 4 additions & 4 deletions frontend/__tests__/unit/components/CardDetailsPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -746,10 +746,10 @@ describe('CardDetailsPage', () => {
render(<CardDetailsPage {...defaultProps} type="chapter" socialLinks={socialLinks} />)

const links = screen.getAllByRole('link')
links.forEach((link) => {
for (const link of links) {
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})
}
})
})

Expand Down Expand Up @@ -1030,10 +1030,10 @@ describe('CardDetailsPage', () => {
const links = screen.getAllByRole('link')
const externalLinks = links.filter((link) => link.getAttribute('href')?.startsWith('http'))

externalLinks.forEach((link) => {
for (const link of externalLinks) {
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})
}
})

it('renders with proper document structure', () => {
Expand Down
8 changes: 4 additions & 4 deletions frontend/__tests__/unit/components/DisplayIcon.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,12 @@ describe('DisplayIcon', () => {
{ item: 'contributionCount', value: 30 },
]

testCases.forEach(({ item, value }) => {
const iconsWithItem: Icon = { [item]: value }
const { container } = render(<DisplayIcon item={item} icons={iconsWithItem} />)
for (const testCase of testCases) {
const iconsWithItem: Icon = { [testCase.item]: testCase.value }
const { container } = render(<DisplayIcon item={testCase.item} icons={iconsWithItem} />)
const containerDiv = container.querySelector('div[class*="flip-container"]')
expect(containerDiv).toBeInTheDocument()
})
}
})

it('applies correct icon classes', () => {
Expand Down
13 changes: 7 additions & 6 deletions frontend/__tests__/unit/components/DonutBarChart.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,18 +303,19 @@ describe('DonutBarChart Component Test Suite', () => {
it('handles different icon types', () => {
const iconTypes = ['chart-pie', 'chart-bar', 'analytics', 'dashboard', 'heart']

iconTypes.forEach((iconType) => {
for (const iconType of iconTypes) {

const { unmount } = render(
<DonutBarChart
icon={iconProp(iconType)}
title={`Test ${iconType}`}
series={[50, 30, 20]}
/>
)

expect(screen.getByTestId('secondary-card')).toHaveAttribute('data-icon', iconType)
unmount()
})
}
})

it('handles various title formats', () => {
Expand All @@ -326,14 +327,14 @@ describe('DonutBarChart Component Test Suite', () => {
'',
]

titles.forEach((title) => {
for (const title of titles) {
const { unmount } = render(
<DonutBarChart icon="chart-pie" title={title} series={[33, 33, 34]} />
)

expect(screen.getByTestId('anchor-title')).toHaveTextContent(title)
unmount()
})
}
})

it('handles large series values', () => {
Expand Down
45 changes: 23 additions & 22 deletions frontend/__tests__/unit/components/Footer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ describe('Footer', () => {
test('renders all footer sections with correct titles', () => {
renderFooter()

mockFooterSections.forEach((section) => {
for (const section of mockFooterSections) {
expect(screen.getByText(section.title)).toBeInTheDocument()
})
}
})

test('renders all section links correctly', () => {
Expand All @@ -150,31 +150,31 @@ describe('Footer', () => {
}
}
}
regularLinks.forEach((link) => {
for (const link of regularLinks) {
const linkElement = screen.getByRole('link', { name: link.text })
expect(linkElement).toBeInTheDocument()
expect(linkElement).toHaveAttribute('href', link.href)
expect(linkElement).toHaveAttribute('target', '_blank')
})
}

spanElements.forEach((link) => {
for (const link of spanElements) {
expect(screen.getByText(link.text)).toBeInTheDocument()
})
}
})

test('renders social media icons with correct attributes', () => {
renderFooter()

mockFooterIcons.forEach((icon) => {
for (const icon of mockFooterIcons) {
const link = screen.getByLabelText(`OWASP Nest ${icon.label}`)
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', icon.href)
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')

const iconElement = link.querySelector('[data-testid="font-awesome-icon"]')
expect(iconElement).toBeInTheDocument()
})
}
})

test('renders copyright information with current year', () => {
Expand Down Expand Up @@ -271,40 +271,41 @@ describe('Footer', () => {
renderFooter()

const buttons = screen.getAllByRole('button')
buttons.forEach((button, index) => {

for (const [index,button] of buttons.entries()) {
const sectionTitle = mockFooterSections[index].title
expect(button).toHaveAttribute('aria-controls', `footer-section-${sectionTitle}`)
expect(button).toHaveAttribute('aria-expanded')
})

}
})

test('has correct section IDs matching aria-controls', () => {
renderFooter()

mockFooterSections.forEach((section) => {
for (const section of mockFooterSections) {
const sectionElement = document.getElementById(`footer-section-${section.title}`)
expect(sectionElement).toBeInTheDocument()
})
}
})

test('has proper semantic structure', () => {
renderFooter()

expect(screen.getByRole('contentinfo')).toBeInTheDocument()

mockFooterSections.forEach((section) => {
for (const section of mockFooterSections) {
const heading = screen.getByRole('heading', { name: section.title, level: 3 })
expect(heading).toBeInTheDocument()
})
}
})

test('has proper aria-labels for social media links', () => {
renderFooter()

mockFooterIcons.forEach((icon) => {
for (const icon of mockFooterIcons) {
const link = screen.getByLabelText(`OWASP Nest ${icon.label}`)
expect(link).toBeInTheDocument()
})
}
})
})

Expand Down Expand Up @@ -335,10 +336,10 @@ describe('Footer', () => {
renderFooter()

const buttons = screen.getAllByRole('button')
buttons.forEach((button) => {
for (const button of buttons) {
expect(button).toHaveClass('flex', 'w-full', 'items-center', 'justify-between')
expect(button).toHaveAttribute('data-disable-animation', 'true')
})
}
})

test('applies correct section content classes for collapsed/expanded states', () => {
Expand Down Expand Up @@ -381,9 +382,9 @@ describe('Footer', () => {

const buttons = screen.getAllByRole('button')
expect(buttons.length).toBeGreaterThan(0)
buttons.forEach((button) => {
for (const button of buttons) {
expect(button).toHaveAttribute('data-disable-animation', 'true')
})
}
})
})
})
4 changes: 2 additions & 2 deletions frontend/__tests__/unit/components/LeadersList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,9 @@ describe('LeadersList Component', () => {

// Check that leader links are inside TruncatedText
const links = screen.getAllByTestId('leader-link')
links.forEach((link) => {
for (const link of links) {
expect(truncatedText).toContainElement(link)
})
}
})

it('generates unique keys for each leader span', () => {
Expand Down
16 changes: 8 additions & 8 deletions frontend/__tests__/unit/components/LogoCarousel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,20 +406,20 @@ describe('MovingLogos (LogoCarousel)', () => {
render(<MovingLogos sponsors={mockSponsors} />)

const links = screen.getAllByTestId('sponsor-link')
links.forEach((link) => {
for (const link of links) {
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})
}
})

it('maintains semantic structure for screen readers', () => {
render(<MovingLogos sponsors={mockSponsors} />)

const sponsorLinks = screen.getAllByTestId('sponsor-link')
sponsorLinks.forEach((link) => {
for (const link of sponsorLinks) {
expect(link).toBeInTheDocument()
expect(link.tagName).toBe('A')
})
}
})

it('provides descriptive text for external links', () => {
Expand Down Expand Up @@ -454,10 +454,10 @@ describe('MovingLogos (LogoCarousel)', () => {
render(<MovingLogos sponsors={mockSponsors} />)

const images = screen.getAllByTestId('sponsor-image')
images.forEach((image) => {
for (const image of images) {
expect(image).toHaveAttribute('style', 'object-fit: contain;')
expect(image).toHaveAttribute('data-fill', 'true')
})
}
})

it('maintains proper DOM hierarchy', () => {
Expand Down Expand Up @@ -514,9 +514,9 @@ describe('MovingLogos (LogoCarousel)', () => {
const sponsorContainers = document.querySelectorAll('[class*="min-w-[220px]"]')
expect(sponsorContainers).toHaveLength(6)

sponsorContainers.forEach((container) => {
for (const container of sponsorContainers) {
expect(container).toHaveClass('min-w-[220px]')
})
}
})
})
})
5 changes: 3 additions & 2 deletions frontend/__tests__/unit/components/MetricsCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ describe('MetricsCard component', () => {
[30, 'text-red-900'],
]

cases.forEach(([score, expectedClass]) => {
for (const [score,expectedClass] of cases) {
const metric = makeMetric({ score })
render(<MetricsCard metric={metric} />)
const scoreEl = screen.getByText(score.toString()).closest('div')
expect(scoreEl).toHaveClass(expectedClass)
})
}

})

it('updates displayed values and link when metric props change via rerender', () => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/__tests__/unit/components/Modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ describe('DialogComp', () => {
const markdownElements = screen.getAllByTestId('markdown')
expect(markdownElements).toHaveLength(2)

markdownElements.forEach((element) => {
for (const element of markdownElements) {
expect(element).toHaveClass('md-wrapper')
})
}
})
})
4 changes: 2 additions & 2 deletions frontend/__tests__/unit/components/ModuleList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ describe('ModuleList', () => {
const modules = ['Module 1', 'Module 2', 'Module 3', 'Module 4', 'Module 5']
render(<ModuleList modules={modules} />)

modules.forEach((module) => {
for (const module of modules) {
Copy link
Collaborator

@kasya kasya Oct 28, 2025

Choose a reason for hiding this comment

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

Not the best idea to name a variable module :)
https://nextjs.org/docs/messages/no-assign-module-variable

Pushed changes to update this and address other issues!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

okay, module is a reserved keyword which can't be used explicitly anywhere.

expect(screen.getByText(module)).toBeInTheDocument()
})
}

// Should not show "Show more" button
expect(screen.queryByText('Show more')).not.toBeInTheDocument()
Expand Down
Loading
Loading