Skip to content

Commit aae09c2

Browse files
authored
test:verify footer responsiveness and accessibility structure JhaSourav07#1514 (JhaSourav07#1943)
## Description Closes JhaSourav07#1514 ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕒 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally. - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors. - [x] My commits follow the Conventional Commits format. - [x] I have made sure that I have only one commit to merge in this PR. ## Visual Preview *(Unit tests written in Vitest to automate structural, accessibility, and breakpoint rendering)* <img width="1322" height="650" alt="Done" src="https://github.com/user-attachments/assets/9d9216a3-da63-4ff2-bc25-44d7d6c5e30a" />
2 parents a32517a + ada5b3b commit aae09c2

5 files changed

Lines changed: 197 additions & 119 deletions

File tree

app/components/Footer.test.tsx

Lines changed: 51 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,82 @@
1-
import { describe, it, expect } from 'vitest';
1+
import '@testing-library/jest-dom/vitest';
22
import { render, screen } from '@testing-library/react';
3+
import { describe, expect, it } from 'vitest';
34
import { Footer } from './Footer';
45

56
describe('Footer Component', () => {
67
it('renders community text', () => {
78
render(<Footer />);
89

9-
const text = screen.getByText(/Designed for the elite builder community/i);
10-
11-
expect(text).toBeTruthy();
10+
expect(screen.getByText(/Designed for the elite builder community/i)).toBeInTheDocument();
1211
});
1312

14-
it('renders Documentation link', () => {
13+
it('renders Documentation link with the correct destination', () => {
1514
render(<Footer />);
1615

17-
const docLink = screen.getByText(/Documentation/i);
18-
19-
expect(docLink).toBeTruthy();
16+
const documentationLink = screen.getByRole('link', {
17+
name: /Documentation/i,
18+
});
2019

21-
expect(docLink.closest('a')?.getAttribute('href')).toBe(
20+
expect(documentationLink).toHaveAttribute(
21+
'href',
2222
'https://github.com/JhaSourav07/commitpulse/blob/main/README.md'
2323
);
2424
});
2525

26-
it('opens documentation in new tab', () => {
26+
it('opens Documentation link in a new tab securely', () => {
2727
render(<Footer />);
2828

29-
const docLink = screen.getByText(/Documentation/i);
29+
const documentationLink = screen.getByRole('link', {
30+
name: /Documentation/i,
31+
});
3032

31-
expect(docLink.closest('a')?.getAttribute('target')).toBe('_blank');
33+
expect(documentationLink).toHaveAttribute('target', '_blank');
34+
expect(documentationLink).toHaveAttribute('rel', expect.stringContaining('noopener'));
35+
expect(documentationLink).toHaveAttribute('rel', expect.stringContaining('noreferrer'));
3236
});
3337

3438
it('renders Contributors link', () => {
3539
render(<Footer />);
3640

37-
const contributorsLink = screen.getByText(/Contributors/i);
38-
39-
expect(contributorsLink).toBeTruthy();
41+
expect(
42+
screen.getByRole('link', {
43+
name: /Contributors/i,
44+
})
45+
).toBeInTheDocument();
4046
});
4147

42-
describe('responsive links and footer tag', () => {
43-
it.each([
44-
['mobile', 375],
45-
['desktop', 1280],
46-
])('renders documented links and the footer tag at the %s breakpoint', (_breakpoint, width) => {
47-
window.innerWidth = width;
48-
49-
const { container } = render(<Footer />);
50-
51-
const layout = container.querySelector('.flex.flex-col.md\\:flex-row');
52-
const contributorsLink = screen.getByRole('link', { name: 'Contributors' });
53-
const documentationLink = screen.getByRole('link', { name: 'Documentation' });
54-
const creatorLink = screen.getByRole('link', { name: 'Creator' });
55-
56-
expect(layout).toBeTruthy();
57-
expect(contributorsLink.getAttribute('href')).toBe('/contributors');
58-
expect(documentationLink.getAttribute('href')).toBe(
59-
'https://github.com/JhaSourav07/commitpulse/blob/main/README.md'
60-
);
61-
expect(creatorLink.getAttribute('href')).toBe('https://github.com/jhasourav07');
62-
expect(screen.getByText(/2026 CommitPulse\. All rights reserved\./i)).toBeTruthy();
63-
});
64-
});
65-
});
66-
it('renders CommitPulse heading', () => {
67-
render(<Footer />);
68-
69-
const heading = screen.getByText('CommitPulse');
70-
71-
expect(heading).toBeTruthy();
72-
});
73-
it('renders Creator link', () => {
74-
render(<Footer />);
48+
it('exposes the footer as a semantic contentinfo landmark for screen readers', () => {
49+
render(<Footer />);
7550

76-
const creatorLink = screen.getByText(/Creator/i);
51+
// A semantic <footer> is exposed to assistive technology as the contentinfo landmark.
52+
const footer = screen.getByRole('contentinfo');
7753

78-
expect(creatorLink).toBeTruthy();
79-
});
80-
it('creator link points to GitHub profile', () => {
81-
render(<Footer />);
82-
83-
const creatorLink = screen.getByText(/Creator/i);
54+
expect(footer).toBeInTheDocument();
55+
expect(footer.tagName).toBe('FOOTER');
56+
});
8457

85-
expect(creatorLink.closest('a')?.getAttribute('href')).toBe('https://github.com/jhasourav07');
86-
});
87-
it('renders copyright text', () => {
88-
render(<Footer />);
58+
it('includes responsive layout classes for mobile and desktop breakpoints', () => {
59+
render(<Footer />);
8960

90-
expect(screen.getByText(/© 2026 CommitPulse. All rights reserved./i)).toBeTruthy();
61+
const footer = screen.getByRole('contentinfo');
62+
63+
// JSDOM cannot calculate Tailwind media queries, so this verifies the actual
64+
// responsive utility classes that switch the footer layout at the md breakpoint.
65+
const responsiveLayout = footer.querySelector('.flex.flex-col.md\\:flex-row.md\\:items-start');
66+
67+
expect(responsiveLayout).toBeInTheDocument();
68+
expect(responsiveLayout).toHaveClass(
69+
'mx-auto',
70+
'flex',
71+
'max-w-6xl',
72+
'flex-col',
73+
'items-center',
74+
'justify-between',
75+
'gap-6',
76+
'text-sm',
77+
'md:flex-row',
78+
'md:items-start',
79+
'md:gap-0'
80+
);
81+
});
9182
});

app/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Footer } from '@/app/components/Footer';
1515

1616
import { FeatureCard, FeatureCardsSection } from '@/components/FeatureCards';
1717
import { DiscordButton } from '@/components/DiscordButton';
18+
1819
import { WallOfLove } from '@/components/WallOfLove';
1920

2021
const Icons = {

components/WallOfLove.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useRef, useState, useEffect, type ReactNode } from 'react';
3+
import { useRef, useState, useEffect } from 'react';
44
import gsap from 'gsap';
55
import { ScrollTrigger } from 'gsap/ScrollTrigger';
66
import { motion } from 'framer-motion';

0 commit comments

Comments
 (0)