1bb4627ab2
CI Pipeline / test (18) (pull_request) Failing after 3m29s
CI Pipeline / test (20) (pull_request) Failing after 4m23s
CI Pipeline / e2e (chromium) (pull_request) Successful in 3m0s
CI Pipeline / e2e (firefox) (pull_request) Successful in 5m45s
CI Pipeline / e2e (webkit) (pull_request) Successful in 4m22s
CI Pipeline / performance (pull_request) Successful in 4m0s
CI Pipeline / storybook (pull_request) Successful in 1m20s
CI Pipeline / visual-regression (pull_request) Successful in 6m7s
CI Pipeline / build (pull_request) Successful in 1m33s
396 lines
9.7 KiB
React
396 lines
9.7 KiB
React
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import RelatedArticles from "../../app/components/RelatedArticles";
|
|
|
|
// Mock Next.js components
|
|
vi.mock("next/link", () => {
|
|
return {
|
|
default: ({ children, href, ...props }) => (
|
|
<a href={href} {...props}>
|
|
{children}
|
|
</a>
|
|
),
|
|
};
|
|
});
|
|
|
|
// Mock ContentThumbnailTemplate
|
|
vi.mock("../../app/components/ContentThumbnailTemplate", () => {
|
|
return {
|
|
default: ({ post }) => (
|
|
<div data-testid={`thumbnail-${post.slug}`}>
|
|
<a href={`/blog/${post.slug}`}>
|
|
<h3>{post.frontmatter.title}</h3>
|
|
<p>{post.frontmatter.description}</p>
|
|
</a>
|
|
</div>
|
|
),
|
|
};
|
|
});
|
|
|
|
// 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(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={postsWithCurrent}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
// 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(
|
|
<RelatedArticles relatedPosts={[]} currentPostSlug="current-article" />,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={currentPostOnly}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
expect(container.firstChild).toBeNull();
|
|
});
|
|
|
|
it("has correct container styling", () => {
|
|
render(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={singlePost}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={twoPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
const section = document.querySelector("section");
|
|
expect(section).toBeInTheDocument();
|
|
});
|
|
|
|
it("applies correct gap between articles", () => {
|
|
render(
|
|
<RelatedArticles
|
|
relatedPosts={mockRelatedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
const carouselContainer = document.querySelector(
|
|
"section > div > div > div",
|
|
);
|
|
expect(carouselContainer).toHaveClass("gap-0");
|
|
});
|
|
|
|
it("handles missing currentPostSlug gracefully", () => {
|
|
render(<RelatedArticles relatedPosts={mockRelatedPosts} />);
|
|
|
|
// 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(
|
|
<RelatedArticles
|
|
relatedPosts={malformedPosts}
|
|
currentPostSlug="current-article"
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByTestId("thumbnail-malformed-1")).toBeInTheDocument();
|
|
expect(screen.getByTestId("thumbnail-malformed-2")).toBeInTheDocument();
|
|
});
|
|
});
|