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:
| Dependency | Mock Implementation |
|---|---|
lucide-react | Icon 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:
| Test | Description |
|---|---|
should render the logo with correct text and link | Logo displays "Img2Num" and links to "/" |
should render all internal navigation links | Home, Credits, About links are present |
should render all external navigation links | Docs and GitHub links are present with correct attributes |
should render the mobile menu toggle button | Hamburger button with proper ARIA attributes |
should render the theme switch | ThemeSwitch 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:
| Test | Description |
|---|---|
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 active | External 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:
| Test | Description |
|---|---|
should open mobile menu when toggle is clicked | Click hamburger → menu opens |
should close mobile menu when toggle is clicked again | Click X → menu closes |
should show backdrop when mobile menu is open | Backdrop appears when menu is open |
should close mobile menu when backdrop is clicked | Click backdrop → menu closes |
should close mobile menu when a navigation link is clicked | Click nav link → menu closes |
should close mobile menu when logo is clicked | Click 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:
| Test | Description |
|---|---|
should have proper ARIA attributes on menu toggle | aria-expanded, aria-controls, aria-label |
should update ARIA attributes when menu is opened | aria-expanded changes to "true" |
should have menubar role on nav list | role="menubar" on <ul> |
should have menuitem role on all links | role="menuitem" on all nav links |
should have proper alt text on logo image | Empty alt (decorative image) |
should mark backdrop as aria-hidden | Backdrop 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');
});
5. External Links Security Tests
Verify secure external link handling:
| Test | Description |
|---|---|
should have proper security attributes on external links | target="_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:
| Test | Description |
|---|---|
should have correct href for internal links | Home → "/", Credits → "/credits", etc. |
should have correct href for external links | GitHub URL, Docs URL |
7. Icons Tests
Verify icon rendering:
| Test | Description |
|---|---|
should render icons for all navigation links | All nav icons present |
should render external link icons | External arrow icons present |
should toggle between Menu and X icons | Icon changes on menu open/close |
8. Structure Tests
Verify semantic structure:
| Test | Description |
|---|---|
should render as a nav element | Component 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
| Category | Tests | Status |
|---|---|---|
| Rendering | 5 | ✅ Passing |
| Active State | 4 | ✅ Passing |
| Mobile Menu Interaction | 6 | ✅ Passing |
| Accessibility | 6 | ✅ Passing |
| External Links Security | 1 | ✅ Passing |
| Navigation | 2 | ✅ Passing |
| Icons | 3 | ✅ Passing |
| Structure | 1 | ✅ Passing |
| Total | 28 | ✅ All Passing |
Adding New Tests
When adding new tests, follow these patterns:
- Use
renderWithRouter- Always wrap NavBar in router context - Query by role - Prefer accessible queries (
getByRole,getByLabelText) - Check CSS classes with regex - CSS modules transform class names
- Use
waitForfor async - Menu animations are async - Test both states - Test before and after user interactions
Related Documentation
- NavBar Component - Main component documentation
- GlassCard - Glassmorphic container wrapper
- ThemeSwitch Testing - Related component tests