import { describe, expect, vi, beforeEach, it } from "vitest"; import { render, screen } from "@testing-library/react"; import RelatedArticles from "../../app/components/RelatedArticles"; // Mock Next.js components vi.mock("next/link", () => { return { default: ({ children, href, ...props }) => ( {children} ), }; }); // Mock ContentThumbnailTemplate vi.mock("../../app/components/ContentThumbnailTemplate", () => { return { default: ({ post }) => (

{post.frontmatter.title}

{post.frontmatter.description}

), }; }); // Mock blog post data const mockRelatedPosts = [ { slug: "related-article-1", frontmatter: { title: "Related Article 1", description: "This is the first related article", author: "Test Author", date: "2025-04-10", }, }, { slug: "related-article-2", frontmatter: { title: "Related Article 2", description: "This is the second related article", author: "Test Author", date: "2025-04-12", }, }, { slug: "related-article-3", frontmatter: { title: "Related Article 3", description: "This is the third related article", author: "Test Author", date: "2025-04-14", }, }, ]; describe("RelatedArticles", () => { beforeEach(() => { // Mock window.innerWidth for responsive tests Object.defineProperty(window, "innerWidth", { writable: true, configurable: true, value: 1024, // Desktop width }); }); it("renders the section with correct structure", () => { render( , ); const section = document.querySelector("section"); expect(section).toBeInTheDocument(); expect(section).toHaveClass( "py-[var(--spacing-scale-032)]", "lg:py-[var(--spacing-scale-064)]", ); }); it("displays the section heading", () => { render( , ); const heading = screen.getByRole("heading", { level: 2 }); expect(heading).toBeInTheDocument(); expect(heading).toHaveTextContent("Related Articles"); expect(heading).toHaveClass( "text-[32px]", "lg:text-[44px]", "leading-[110%]", "font-medium", "text-[var(--color-content-inverse-primary)]", "text-center", ); }); it("renders all related articles", () => { render( , ); expect( screen.getByTestId("thumbnail-related-article-1"), ).toBeInTheDocument(); expect( screen.getByTestId("thumbnail-related-article-2"), ).toBeInTheDocument(); expect( screen.getByTestId("thumbnail-related-article-3"), ).toBeInTheDocument(); }); it("filters out the current post from related articles", () => { const postsWithCurrent = [ ...mockRelatedPosts, { slug: "current-article", frontmatter: { title: "Current Article", description: "This is the current article", author: "Test Author", date: "2025-04-15", }, }, ]; render( , ); // Should not render the current article expect( screen.queryByTestId("thumbnail-current-article"), ).not.toBeInTheDocument(); // Should still render the other related articles expect( screen.getByTestId("thumbnail-related-article-1"), ).toBeInTheDocument(); expect( screen.getByTestId("thumbnail-related-article-2"), ).toBeInTheDocument(); expect( screen.getByTestId("thumbnail-related-article-3"), ).toBeInTheDocument(); }); it("renders nothing when no related posts", () => { const { container } = render( , ); expect(container.firstChild).toBeNull(); }); it("renders nothing when all posts are filtered out", () => { const currentPostOnly = [ { slug: "current-article", frontmatter: { title: "Current Article", description: "This is the current article", author: "Test Author", date: "2025-04-15", }, }, ]; const { container } = render( , ); expect(container.firstChild).toBeNull(); }); it("has correct container styling", () => { render( , ); const container = document.querySelector("section > div"); expect(container).toHaveClass( "flex", "flex-col", "gap-[var(--spacing-scale-032)]", "lg:gap-[51px]", ); }); it("has correct articles container styling", () => { render( , ); const articlesContainer = document.querySelector("section > div > div"); expect(articlesContainer).toHaveClass( "flex", "justify-center", "overflow-hidden", ); }); it("applies correct responsive behavior for desktop", () => { // Set desktop width (must be > 1024px to be desktop, since lg breakpoint is 1024px) Object.defineProperty(window, "innerWidth", { writable: true, configurable: true, value: 1200, }); render( , ); const carouselContainer = document.querySelector( "section > div > div > div", ); expect(carouselContainer).toHaveClass( "overflow-x-auto", "scrollbar-hide", "cursor-grab", "active:cursor-grabbing", ); }); it("applies correct responsive behavior for mobile", () => { // Set mobile width Object.defineProperty(window, "innerWidth", { writable: true, configurable: true, value: 768, }); render( , ); const carouselContainer = document.querySelector( "section > div > div > div", ); expect(carouselContainer).toHaveClass( "transition-transform", "duration-500", "ease-in-out", ); }); it("handles single related article", () => { const singlePost = [mockRelatedPosts[0]]; render( , ); expect( screen.getByTestId("thumbnail-related-article-1"), ).toBeInTheDocument(); expect( screen.queryByTestId("thumbnail-related-article-2"), ).not.toBeInTheDocument(); expect( screen.queryByTestId("thumbnail-related-article-3"), ).not.toBeInTheDocument(); }); it("handles two related articles", () => { const twoPosts = mockRelatedPosts.slice(0, 2); render( , ); expect( screen.getByTestId("thumbnail-related-article-1"), ).toBeInTheDocument(); expect( screen.getByTestId("thumbnail-related-article-2"), ).toBeInTheDocument(); expect( screen.queryByTestId("thumbnail-related-article-3"), ).not.toBeInTheDocument(); }); it("has proper accessibility attributes", () => { render( , ); const section = document.querySelector("section"); expect(section).toBeInTheDocument(); }); it("applies correct gap between articles", () => { render( , ); const carouselContainer = document.querySelector( "section > div > div > div", ); expect(carouselContainer).toHaveClass("gap-0"); }); it("handles missing currentPostSlug gracefully", () => { render(); // Should still render all articles expect( screen.getByTestId("thumbnail-related-article-1"), ).toBeInTheDocument(); expect( screen.getByTestId("thumbnail-related-article-2"), ).toBeInTheDocument(); expect( screen.getByTestId("thumbnail-related-article-3"), ).toBeInTheDocument(); }); it("handles malformed post data gracefully", () => { const malformedPosts = [ { slug: "malformed-1", frontmatter: { title: "Malformed Post 1", description: "Test description", author: "Test Author", date: "2025-04-15", }, }, { slug: "malformed-2", frontmatter: { title: "Malformed Post 2", description: "Test description", author: "Test Author", date: "2025-04-15", }, }, ]; render( , ); expect(screen.getByTestId("thumbnail-malformed-1")).toBeInTheDocument(); expect(screen.getByTestId("thumbnail-malformed-2")).toBeInTheDocument(); }); });