Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 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) => {
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
10 changes: 5 additions & 5 deletions frontend/__tests__/unit/components/Badges.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@ 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
36 changes: 10 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,16 @@ 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
8 changes: 4 additions & 4 deletions frontend/__tests__/unit/components/DonutBarChart.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ 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)}
Expand All @@ -314,7 +314,7 @@ describe('DonutBarChart Component Test Suite', () => {

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

it('handles various title formats', () => {
Expand All @@ -326,14 +326,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
42 changes: 21 additions & 21 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,22 +150,22 @@ 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)
Expand All @@ -174,7 +174,7 @@ describe('Footer', () => {

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,40 @@ 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 +335,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 +381,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]')
})
}
})
})
})
4 changes: 2 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,12 @@ 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')
})
}
})
})
6 changes: 3 additions & 3 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) => {
expect(screen.getByText(module)).toBeInTheDocument()
})
for (const myModule of modules) {
expect(screen.getByText(myModule)).toBeInTheDocument()
}

// Should not show "Show more" button
expect(screen.queryByText('Show more')).not.toBeInTheDocument()
Expand Down
5 changes: 3 additions & 2 deletions frontend/__tests__/unit/components/MultiSearch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,10 @@ describe('Rendering', () => {
await waitFor(() => {
const suggestions = screen.getAllByText('Test Chapter')
expect(suggestions.length).toBeGreaterThan(0)
suggestions.forEach((suggestion) => {

for (const suggestion of suggestions) {
expect(suggestion).toBeInTheDocument()
})
}
})

await user.click(document.body)
Expand Down
Loading