Skip to main content

NavBar Testing

This document describes the test suite for the NavBar component, covering rendering, interactions, accessibility, and edge cases.

Test File Location

src/components/NavBar.test.jsx

Test Setup

The tests use the following testing utilities:

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import NavBar from './NavBar';

Mocked Dependencies

The following dependencies are mocked for testing:

DependencyMock Implementation
lucide-reactIcon components return spans with data-testid
vi.mock('lucide-react', () => ({
Home: () => <span data-testid="home-icon">Home</span>,
Users: () => <span data-testid="users-icon">Users</span>,
Info: () => <span data-testid="info-icon">Info</span>,
Github: () => <span data-testid="github-icon">Github</span>,
SquareArrowOutUpRight: () => <span data-testid="external-icon">External</span>,
Menu: () => <span data-testid="menu-icon">Menu</span>,
X: () => <span data-testid="close-icon">X</span>,
Sun: () => <span data-testid="sun-icon">Sun</span>,
Moon: () => <span data-testid="moon-icon">Moon</span>,
}));

Test Utilities

A helper function wraps components with the router provider:

const renderWithRouter = (component, { route = '/' } = {}) => {
return render(<MemoryRouter initialEntries={[route]}>{component}</MemoryRouter>);
};

Test Categories

1. Rendering Tests

Verify that all elements render correctly:

TestDescription
should render the logo with correct text and linkLogo displays "Img2Num" and links to "/"
should render all internal navigation linksHome, Credits, About links are present
should render all external navigation linksDocs and GitHub links are present with correct attributes
should render the mobile menu toggle buttonHamburger button with proper ARIA attributes
should render the theme switchThemeSwitch component is rendered

Example:

it('should render all internal navigation links', () => {
renderWithRouter(<NavBar />);

expect(screen.getByRole('menuitem', { name: /home/i })).toBeInTheDocument();
expect(screen.getByRole('menuitem', { name: /credits/i })).toBeInTheDocument();
expect(screen.getByRole('menuitem', { name: /about/i })).toBeInTheDocument();
});

2. Active State Tests

Verify route-based styling:

TestDescription
should mark the home link as active when on home page"/" route highlights Home
should mark the credits link as active when on credits page"/credits" highlights Credits
should mark the about link as active when on about page"/about" highlights About
should not mark external links as activeExternal links never show active state

Example:

it('should mark the home link as active when on home page', () => {
renderWithRouter(<NavBar />, { route: '/' });

const homeLink = screen.getByRole('menuitem', { name: /home/i });
expect(homeLink.className).toMatch(/active/);
});
CSS Modules

Since CSS modules transform class names, tests check for partial class name matches using regex instead of exact class names.

3. Mobile Menu Interaction Tests

Verify hamburger menu functionality:

TestDescription
should open mobile menu when toggle is clickedClick hamburger → menu opens
should close mobile menu when toggle is clicked againClick X → menu closes
should show backdrop when mobile menu is openBackdrop appears when menu is open
should close mobile menu when backdrop is clickedClick backdrop → menu closes
should close mobile menu when a navigation link is clickedClick nav link → menu closes
should close mobile menu when logo is clickedClick logo → menu closes

Example:

it('should open mobile menu when toggle is clicked', async () => {
renderWithRouter(<NavBar />);

const menuToggle = screen.getByRole('button', { name: /open menu/i });
const navList = screen.getByRole('menubar');

// Initially closed
expect(menuToggle).toHaveAttribute('aria-expanded', 'false');
expect(navList.className).not.toMatch(/open/);

// Click to open
fireEvent.click(menuToggle);

await waitFor(() => {
expect(menuToggle).toHaveAttribute('aria-expanded', 'true');
expect(navList.className).toMatch(/open/);
});
});

4. Accessibility Tests

Verify ARIA compliance:

TestDescription
should have proper ARIA attributes on menu togglearia-expanded, aria-controls, aria-label
should update ARIA attributes when menu is openedaria-expanded changes to "true"
should have menubar role on nav listrole="menubar" on <ul>
should have menuitem role on all linksrole="menuitem" on all nav links
should have proper alt text on logo imageEmpty alt (decorative image)
should mark backdrop as aria-hiddenBackdrop has aria-hidden="true"

Example:

it('should have proper ARIA attributes on menu toggle', () => {
renderWithRouter(<NavBar />);

const menuToggle = screen.getByRole('button', { name: /open menu/i });

expect(menuToggle).toHaveAttribute('aria-expanded', 'false');
expect(menuToggle).toHaveAttribute('aria-controls', 'nav-menu');
expect(menuToggle).toHaveAttribute('aria-label', 'Open menu');
});

Verify secure external link handling:

TestDescription
should have proper security attributes on external linkstarget="_blank" + rel="noopener noreferrer"

Example:

it('should have proper security attributes on external links', () => {
renderWithRouter(<NavBar />);

const externalLinks = screen
.getAllByRole('menuitem')
.filter((link) => link.hasAttribute('target') && link.getAttribute('target') === '_blank');

externalLinks.forEach((link) => {
expect(link).toHaveAttribute('rel', 'noopener noreferrer');
});
});

6. Navigation Tests

Verify link destinations:

TestDescription
should have correct href for internal linksHome → "/", Credits → "/credits", etc.
should have correct href for external linksGitHub URL, Docs URL

7. Icons Tests

Verify icon rendering:

TestDescription
should render icons for all navigation linksAll nav icons present
should render external link iconsExternal arrow icons present
should toggle between Menu and X iconsIcon changes on menu open/close

8. Structure Tests

Verify semantic structure:

TestDescription
should render as a nav elementComponent uses <nav> tag

Running the Tests

# Run NavBar tests only
npm test -- NavBar.test.jsx

# Run all component tests
npm test

# Run with coverage
npm test -- --coverage

Test Results Summary

CategoryTestsStatus
Rendering5✅ Passing
Active State4✅ Passing
Mobile Menu Interaction6✅ Passing
Accessibility6✅ Passing
External Links Security1✅ Passing
Navigation2✅ Passing
Icons3✅ Passing
Structure1✅ Passing
Total28All Passing

Adding New Tests

When adding new tests, follow these patterns:

  1. Use renderWithRouter - Always wrap NavBar in router context
  2. Query by role - Prefer accessible queries (getByRole, getByLabelText)
  3. Check CSS classes with regex - CSS modules transform class names
  4. Use waitFor for async - Menu animations are async
  5. Test both states - Test before and after user interactions