Skip to content
This repository was archived by the owner on Mar 13, 2026. It is now read-only.

Commit d672aa6

Browse files
Merge pull request #103 from Jordan231111/chore/move-e2e-refactor-2025
chore: consolidate e2e Playwright tests, fix nav anchor issues, add guardrail suites (a11y/perf/visual), and docs
2 parents 80baf5c + a1463a5 commit d672aa6

36 files changed

Lines changed: 701 additions & 1000 deletions

__tests__/__mocks__/mockRegistry.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@ import extendedMocks from "./extendedMocks";
1717
*/
1818
export const MockRegistrationLink = ({ className }: { className?: string }) => {
1919
return (
20-
<div data-testid="registration-link" className={className} role="link" aria-label="Registration Link">
21-
Registration Link
22-
</div>
20+
<a
21+
data-testid="registration-link"
22+
className={className}
23+
href="https://hackrpi2025.devpost.com/"
24+
target="_blank"
25+
rel="noopener noreferrer"
26+
>
27+
Register Here!
28+
</a>
2329
);
2430
};
2531

__tests__/components/about-us.test.tsx

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,6 @@ jest.mock("next/navigation", () => ({
2222
usePathname: () => "/",
2323
}));
2424

25-
jest.mock("@/components/themed-components/registration-link", () => {
26-
return {
27-
__esModule: true,
28-
default: ({ children, className }: { children?: React.ReactNode; className?: string }) => (
29-
<div data-testid="registration-link" className={className} role="link" aria-label="Registration Link">
30-
{children || "Registration Link"}
31-
</div>
32-
),
33-
};
34-
});
35-
3625
// Define the current theme and year for better test maintainability
3726
const CURRENT_THEME = "Retro vs. Modern";
3827
const HACKRPI_YEAR = getCurrentHackrpiYear();
@@ -109,22 +98,16 @@ describe("AboutUs Component", () => {
10998
expect(parentContainer).toContainElement(venueElement);
11099
});
111100

112-
it("renders the registration link with correct styling", () => {
113-
// 2025 best practice: Render the component and get the container
101+
it("renders the registration banner with correct styling", () => {
114102
const { container } = renderWithProviders(<AboutUs />);
115103

116-
// 2025 best practice: Use data-testid for more reliable selection
117-
const registrationLink = screen.getByTestId("registration-link");
118-
expect(registrationLink).toBeInTheDocument();
119-
expect(registrationLink).toHaveClass("text-xl");
120-
121-
// 2025 best practice: Find the REGISTER NOW text using a pattern
122-
const registerNowText = screen.getByText(/REGISTER NOW!/i);
123-
expect(registerNowText).toBeInTheDocument();
104+
const registerBanner = screen.getByTestId("register-now-banner");
105+
expect(registerBanner).toBeInTheDocument();
106+
expect(registerBanner).toHaveTextContent(/REGISTER NOW!/i);
107+
expect(registerBanner).toHaveClass("bg-hackrpi-secondary-orange");
108+
expect(registerBanner).toHaveClass("text-white");
124109

125-
// Verify they are both in the document but don't assert they're in the same container
126-
expect(container).toContainElement(registrationLink);
127-
expect(container).toContainElement(registerNowText);
110+
expect(container).toContainElement(registerBanner);
128111
});
129112

130113
it('renders the scrolling "REGISTER NOW!" text with correct styling', () => {
@@ -137,7 +120,7 @@ describe("AboutUs Component", () => {
137120

138121
// Check styling directly on the element with data-testid
139122
expect(registerBanner).toHaveClass("bg-hackrpi-secondary-orange");
140-
expect(registerBanner).toHaveClass("text-black");
123+
expect(registerBanner).toHaveClass("text-white");
141124
expect(registerBanner).toHaveClass("overflow-hidden");
142125
expect(registerBanner).toHaveClass("whitespace-nowrap");
143126
});
@@ -164,11 +147,8 @@ describe("AboutUs Component", () => {
164147
const { container } = renderWithProviders(<AboutUs />);
165148

166149
// 2025 best practice: Test for basic accessibility patterns
167-
const links = screen.getAllByRole("link");
168-
expect(links.length).toBeGreaterThan(0);
169-
links.forEach((link) => {
170-
expect(link).toHaveAccessibleName();
171-
});
150+
const links = screen.queryAllByRole("link");
151+
expect(links.length).toBe(0);
172152

173153
const headings = screen.getAllByRole("heading");
174154
expect(headings.length).toBeGreaterThan(1);
@@ -187,7 +167,7 @@ describe("AboutUs Component", () => {
187167

188168
// Check that key elements are still visible on mobile
189169
expect(screen.getByRole("heading", { name: /About HackRPI/i })).toBeInTheDocument();
190-
expect(screen.getByTestId("registration-link")).toBeInTheDocument();
170+
expect(screen.getByTestId("register-now-banner")).toBeInTheDocument();
191171

192172
// Clean up mobile test and set up desktop test
193173
cleanup();
@@ -197,7 +177,7 @@ describe("AboutUs Component", () => {
197177

198178
// Verify desktop layout elements
199179
expect(screen.getByRole("heading", { name: /About HackRPI/i })).toBeInTheDocument();
200-
expect(screen.getByTestId("registration-link")).toBeInTheDocument();
180+
expect(screen.getByTestId("register-now-banner")).toBeInTheDocument();
201181
});
202182

203183
// 2025 Best Practice: Add automated accessibility testing

__tests__/components/event.test.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jest.mock("next/image", () => ({
6161
}));
6262

6363
// Import the component after all mocks are defined
64-
import EventPage from "@/app/event/page";
64+
import EventPage from "@/app/(with-layout)/event/page";
6565

6666
describe("Event Page", () => {
6767
beforeEach(() => {
@@ -71,11 +71,9 @@ describe("Event Page", () => {
7171
it("renders the main layout components", () => {
7272
render(<EventPage />);
7373

74-
// Check if the navbar component is rendered
75-
expect(screen.getByTestId("nav-bar")).toBeInTheDocument();
76-
77-
// Note: Footer is imported but not actually used in the component
78-
// so we should not expect it in the test
74+
// Verify key structural headings render
75+
expect(screen.getByText("Location:")).toBeInTheDocument();
76+
expect(screen.getByText("Need Help?")).toBeInTheDocument();
7977
});
8078

8179
it("renders the map component", () => {

__tests__/components/faq.test.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@ import "@testing-library/jest-dom";
88
jest.mock("@/components/themed-components/registration-link", () => {
99
return {
1010
__esModule: true,
11-
default: ({ children, className }: { children?: React.ReactNode; className?: string }) => (
12-
<div data-testid="registration-link" className={className} role="link" aria-label="Registration Link">
13-
{children || "Registration Link"}
14-
</div>
11+
default: ({ className }: { className?: string }) => (
12+
<a
13+
data-testid="registration-link"
14+
href="https://hackrpi2025.devpost.com/"
15+
target="_blank"
16+
rel="noopener noreferrer"
17+
className={className}
18+
>
19+
Register Here!
20+
</a>
1521
),
1622
};
1723
});
@@ -95,8 +101,9 @@ describe("FAQ Component", () => {
95101
fireEvent.click(registrationFAQ);
96102

97103
// Check if the registration link is rendered
98-
const registrationLink = screen.getByTestId("registration-link");
104+
const registrationLink = screen.getByRole("link", { name: /register here!/i });
99105
expect(registrationLink).toBeInTheDocument();
106+
expect(registrationLink).toHaveAttribute("href", "https://hackrpi2025.devpost.com/");
100107
});
101108

102109
it("renders the contact information at the bottom", () => {

__tests__/components/footer.test.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ jest.mock("next/image", () => ({
1111
}));
1212

1313
// Mock logo import
14-
jest.mock("@/public/HackRPI_Logo_Yellow_Arrow.png", () => ({
15-
default: "mock-logo-path",
14+
jest.mock("@/public/Retro_HackRPI_Logo.png", () => ({
15+
default: "mock-retro-logo",
1616
}));
1717

1818
jest.mock("@/components/socials-links/social-links", () => {
@@ -24,9 +24,15 @@ jest.mock("@/components/socials-links/social-links", () => {
2424
jest.mock("@/components/themed-components/registration-link", () => {
2525
return function MockRegistrationLink({ className }: { className?: string }) {
2626
return (
27-
<div data-testid="registration-link" className={className}>
28-
Registration Link
29-
</div>
27+
<a
28+
data-testid="registration-link"
29+
className={className}
30+
href="https://hackrpi2025.devpost.com/"
31+
target="_blank"
32+
rel="noopener noreferrer"
33+
>
34+
Register Here!
35+
</a>
3036
);
3137
};
3238
});
@@ -72,6 +78,7 @@ describe("Footer Component", () => {
7278
expect(registrationLink).toBeInTheDocument();
7379
expect(registrationLink).toHaveClass("text-xl");
7480
expect(registrationLink).toHaveClass("mb-4");
81+
expect(registrationLink).toHaveAttribute("href", "https://hackrpi2025.devpost.com/");
7582
});
7683

7784
it("renders the social links", () => {

__tests__/components/last-year.test.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,23 @@ jest.mock("@/components/themed-components/hackrpi-link", () => {
8080
};
8181
});
8282

83+
jest.mock("@/components/prev-projects/LastYearCollage", () => {
84+
return function MockLastYearCollage() {
85+
return (
86+
<div data-testid="photo-gallery">
87+
{Array.from({ length: 12 }).map((_, index) => (
88+
<img
89+
key={index}
90+
data-testid="mock-image"
91+
src={`/lastYearPhotos/photo-${index + 1}.jpg`}
92+
alt={`HackRPI XI Photo ${index + 1}`}
93+
/>
94+
))}
95+
</div>
96+
);
97+
};
98+
});
99+
83100
jest.mock("next/image", () => ({
84101
__esModule: true,
85102
default: (props: any) => {
@@ -88,7 +105,7 @@ jest.mock("next/image", () => ({
88105
}));
89106

90107
// Import the component after all mocks are defined
91-
import PastYearProjects from "@/app/last-year/page";
108+
import PastYearProjects from "@/app/(with-layout)/last-year/page";
92109

93110
describe("Last Year Projects Page", () => {
94111
beforeEach(() => {

__tests__/components/nav-bar.test.tsx

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22
import { render, screen, cleanup } from "@testing-library/react";
3-
import NavBar from "@/components/nav-bar/nav-bar";
3+
import NavBar, { links as navLinks } from "@/components/nav-bar/nav-bar";
44
import { renderWithProviders, resetAllMocks, setWindowDimensions } from "../test-utils";
55

66
/**
@@ -42,29 +42,6 @@ jest.mock("@/data/nav-bar-links", () => {
4242
};
4343
});
4444

45-
// Mock the links directly in the NavBar component
46-
jest.mock("@/components/nav-bar/nav-bar", () => {
47-
const originalModule = jest.requireActual("@/components/nav-bar/nav-bar");
48-
return {
49-
__esModule: true,
50-
...originalModule,
51-
links: [
52-
{
53-
name: "Home",
54-
links: [
55-
{ href: "/", children: "Home" },
56-
{ href: "/#about", children: "About" },
57-
],
58-
},
59-
{
60-
name: "HackRPI XI",
61-
links: [{ href: "/last-year#winners", children: "Winners" }],
62-
},
63-
],
64-
default: originalModule.default,
65-
};
66-
});
67-
6845
// Mock MLH Banner
6946
jest.mock("@/components/mlh-banner/mlh-banner", () => {
7047
return function MockMlhBanner() {
@@ -134,9 +111,10 @@ describe("NavBar Component", () => {
134111
// Act - Render the component
135112
renderWithProviders(<NavBar showOnScroll={true} />);
136113

137-
// Assert - Check if links are passed correctly - we expect 2 links based on our mock
114+
// Assert - Check if links are passed correctly based on the exported navigation data
138115
const mobileNav = screen.getByTestId("nav-bar-mobile");
139-
expect(mobileNav.textContent).toContain("2 links");
116+
const expectedCount = navLinks.length;
117+
expect(mobileNav.textContent).toContain(`${expectedCount} links`);
140118

141119
// Clean up before rendering again
142120
cleanup();
@@ -146,7 +124,7 @@ describe("NavBar Component", () => {
146124
renderWithProviders(<NavBar showOnScroll={true} />);
147125

148126
const desktopNav = screen.getByTestId("nav-bar-desktop");
149-
expect(desktopNav.textContent).toContain("2 links");
127+
expect(desktopNav.textContent).toContain(`${expectedCount} links`);
150128
});
151129

152130
it("should handle showOnScroll prop correctly", async () => {

__tests__/components/nav-bar/desktop/nav-bar-desktop.test.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { render, screen } from "@testing-library/react";
22
import "@testing-library/jest-dom";
33

44
// Mock the image imports
5-
jest.mock("@/public/HackRPI_Logo_Yellow_Arrow.png", () => "logo-image-stub");
5+
jest.mock("@/public/Retro_HackRPI_Logo.png", () => "logo-image-stub");
66
jest.mock("next/image", () => ({
77
__esModule: true,
88
default: (props: any) => {
@@ -12,11 +12,6 @@ jest.mock("next/image", () => ({
1212
}));
1313

1414
// Mock the registration button
15-
jest.mock("@/components/themed-components/registration-link", () => ({
16-
__esModule: true,
17-
default: () => <div data-testid="registration-button">Register Now</div>,
18-
}));
19-
2015
// Mock NavGroupComponent
2116
jest.mock("@/components/nav-bar/desktop/nav-group", () => ({
2217
__esModule: true,
@@ -70,19 +65,15 @@ describe("DesktopNavBar Component", () => {
7065

7166
// Check direct links
7267
expect(screen.getByText("Sponsor Us")).toBeInTheDocument();
73-
expect(screen.getByText("Event Info")).toBeInTheDocument();
74-
expect(screen.getByText("Schedule")).toBeInTheDocument();
75-
expect(screen.getByText("Announcements")).toBeInTheDocument();
76-
expect(screen.getByText("Prizes")).toBeInTheDocument();
77-
expect(screen.getByText("2048 Leaderboard")).toBeInTheDocument();
7868
expect(screen.getByText("Code of Conduct")).toBeInTheDocument();
7969
});
8070

81-
it("includes registration button", () => {
71+
it("includes registration link", () => {
8272
render(<DesktopNavBar links={mockLinks} />);
8373

84-
// Check for the registration button
85-
expect(screen.getByTestId("registration-button")).toBeInTheDocument();
74+
const registerLink = screen.getByRole("link", { name: /register/i });
75+
expect(registerLink).toHaveAttribute("href", "https://hackrpi2024.devpost.com/project-gallery");
76+
expect(registerLink).toHaveAttribute("target", "_blank");
8677
});
8778

8879
it("applies correct styling to the navbar", () => {

__tests__/components/resources.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jest.mock("next/image", () => ({
4646
}));
4747

4848
// Import the component after all mocks are defined
49-
import ResourcesPage from "@/app/resources/page";
49+
import ResourcesPage from "@/app/(with-layout)/resources/page";
5050

5151
describe("Resources Page", () => {
5252
beforeEach(() => {
@@ -56,9 +56,9 @@ describe("Resources Page", () => {
5656
it("renders the main layout components", () => {
5757
render(<ResourcesPage />);
5858

59-
// Check if the main structural components are rendered
60-
expect(screen.getByTestId("nav-bar")).toBeInTheDocument();
61-
// Footer is imported but not actually used in the component
59+
// Verify primary headings render
60+
expect(screen.getByText("Web Development")).toBeInTheDocument();
61+
expect(screen.getByText("Submitting Your Project")).toBeInTheDocument();
6262
});
6363

6464
it("renders all resource section headings", () => {

__tests__/components/schedule.test.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ describe("Schedule Component", () => {
2323
endHour: number,
2424
eventType: string = "default",
2525
visible: boolean = true,
26+
column: number = 1,
2627
): Event => ({
2728
id,
2829
title: `Event ${id}`,
@@ -33,7 +34,8 @@ describe("Schedule Component", () => {
3334
speaker: startHour % 2 === 0 ? `Speaker ${id}` : "", // Alternate between having a speaker and not
3435
eventType,
3536
visible,
36-
column: 0,
37+
column,
38+
width: 1,
3739
});
3840

3941
// Sample data for testing
@@ -250,10 +252,10 @@ describe("Schedule Component", () => {
250252

251253
it("renders multiple columns for overlapping events", () => {
252254
// Create events that overlap
253-
const overlappingEvents = [
254-
createEvent("1", 10, 12, "default"), // 10:00 AM - 12:00 PM
255-
createEvent("2", 11, 13, "workshop"), // 11:00 AM - 1:00 PM - overlaps with event 1
256-
];
255+
const overlappingEvents = [
256+
createEvent("1", 10, 12, "default", true, 1), // 10:00 AM - 12:00 PM
257+
createEvent("2", 11, 13, "workshop", true, 2), // 11:00 AM - 1:00 PM - overlaps with event 1
258+
];
257259

258260
render(
259261
<Schedule

0 commit comments

Comments
 (0)