Files
community-rule/tests/unit/ContentContainer.test.jsx
T
2026-05-20 23:01:55 -06:00

277 lines
8.5 KiB
React

import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import ContentContainer from "../../app/components/content/ContentContainer";
// Mock asset utils
vi.mock("../../lib/assetUtils", () => ({
contentBlogTagPath: vi.fn((slug) => `/content/blog/${slug}-tag.svg`),
contentCatalogSlugForFallback: vi.fn((slug) => {
const catalog = [
"resolving-active-conflicts",
"operational-security-mutual-aid",
"making-decisions-without-hierarchy",
];
if (!slug) return catalog[0];
const index =
Math.abs(
slug.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0),
) % catalog.length;
return catalog[index];
}),
CONTENT_CATALOG_SLUG_ORDER: [
"resolving-active-conflicts",
"operational-security-mutual-aid",
"making-decisions-without-hierarchy",
],
}));
// Mock blog post data
const mockPost = {
slug: "test-article",
frontmatter: {
title: "Test Article Title",
description:
"This is a test article description that should be long enough to test truncation and wrapping behavior.",
author: "Test Author",
date: "2025-04-15",
},
};
// Pure presentational; no provider context needed.
describe("ContentContainer", () => {
it("renders with default props", () => {
render(<ContentContainer post={mockPost} />);
// Check that the container exists
const container = document.querySelector("div[class*='relative z-20']");
expect(container).toBeInTheDocument();
expect(container).toHaveClass(
"relative",
"z-20",
"h-full",
"flex",
"flex-col",
);
});
it("displays the icon correctly", () => {
render(<ContentContainer post={mockPost} />);
const icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toBeInTheDocument();
expect(icon).toHaveAttribute(
"src",
"/content/blog/resolving-active-conflicts-tag.svg",
);
expect(icon).toHaveClass("w-[60px]", "h-[30px]", "object-contain");
});
it("displays the article title", () => {
render(<ContentContainer post={mockPost} />);
const title = screen.getByText("Test Article Title");
expect(title).toBeInTheDocument();
expect(title).toHaveClass(
"font-bricolage",
"font-medium",
"text-[18px]",
"leading-[120%]",
"text-[var(--color-content-inverse-brand-royal)]",
);
});
it("displays the article description", () => {
render(<ContentContainer post={mockPost} />);
const description = screen.getByText(/This is a test article description/);
expect(description).toBeInTheDocument();
expect(description).toHaveClass(
"font-inter",
"font-normal",
"text-[12px]",
"leading-[16px]",
"text-[var(--color-content-inverse-brand-royal)]",
);
});
it("displays the author and date metadata", () => {
render(<ContentContainer post={mockPost} />);
expect(screen.getByText("Test Author")).toBeInTheDocument();
expect(screen.getByText("April 2025")).toBeInTheDocument();
});
it("applies correct width when specified", () => {
render(<ContentContainer post={mockPost} width="300px" size="xs" />);
const container = document.querySelector("div[class*='relative z-20']");
expect(container).toHaveStyle("width: 300px");
});
it("applies default width when not specified", () => {
render(<ContentContainer post={mockPost} size="xs" />);
const container = document.querySelector("div[class*='relative z-20']");
expect(container).toHaveStyle("width: 200px");
});
it("has proper spacing between icon and text", () => {
render(<ContentContainer post={mockPost} />);
const iconContainer = screen
.getByAltText("Icon for Test Article Title")
.closest("div");
const textContainer = screen.getByText("Test Article Title").closest("div");
// Check the content container (parent of icon)
expect(iconContainer.parentElement).toHaveClass(
"gap-[var(--measures-spacing-008)]",
);
// Check the text container (parent of title) - it has responsive gap classes
expect(textContainer.parentElement).toHaveClass("flex", "flex-col");
});
it("has proper metadata container styling", () => {
render(<ContentContainer post={mockPost} />);
const metadataContainer = screen.getByText("Test Author").closest("div");
expect(metadataContainer).toHaveClass(
"flex",
"min-w-0",
"items-end",
"gap-[var(--measures-spacing-008)]",
);
});
it("applies correct metadata text styling", () => {
render(<ContentContainer post={mockPost} />);
const author = screen.getByText("Test Author");
expect(author).toHaveClass(
"font-inter",
"font-normal",
"text-[10px]",
"leading-[14px]",
"text-[var(--color-content-inverse-brand-royal)]",
);
const date = screen.getByText("April 2025");
expect(date).toHaveClass(
"font-inter",
"font-normal",
"text-[10px]",
"leading-[14px]",
"text-[var(--color-content-inverse-brand-royal)]",
);
});
it("uses per-article tag assets for catalog slugs", () => {
const { rerender } = render(<ContentContainer post={mockPost} />);
let icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveAttribute(
"src",
"/content/blog/resolving-active-conflicts-tag.svg",
);
const post2 = { ...mockPost, slug: "operational-security-mutual-aid" };
rerender(<ContentContainer post={post2} />);
icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveAttribute(
"src",
"/content/blog/operational-security-mutual-aid-tag.svg",
);
const post3 = { ...mockPost, slug: "making-decisions-without-hierarchy" };
rerender(<ContentContainer post={post3} />);
icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveAttribute(
"src",
"/content/blog/making-decisions-without-hierarchy-tag.svg",
);
});
it("handles missing post data gracefully", () => {
const incompletePost = {
slug: "incomplete",
frontmatter: {
title: "Incomplete Post",
// Missing other fields
},
};
render(<ContentContainer post={incompletePost} />);
expect(screen.getByText("Incomplete Post")).toBeInTheDocument();
});
it("applies correct responsive sizing for xs breakpoint", () => {
render(<ContentContainer post={mockPost} size="xs" />);
const icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveClass("w-[60px]", "h-[30px]");
const title = screen.getByText("Test Article Title");
expect(title).toHaveClass("text-[18px]", "leading-[22px]");
const description = screen.getByText(/This is a test article description/);
expect(description).toHaveClass("text-[12px]", "leading-[16px]");
});
it("applies correct responsive sizing for responsive breakpoint", () => {
render(<ContentContainer post={mockPost} size="responsive" />);
const icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveClass("w-[60px]", "h-[30px]");
const title = screen.getByText("Test Article Title");
expect(title).toHaveClass("text-[18px]", "leading-[120%]");
const description = screen.getByText(/This is a test article description/);
expect(description).toHaveClass("text-[12px]", "leading-[16px]");
});
it("has proper accessibility attributes", () => {
render(<ContentContainer post={mockPost} />);
const icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveAttribute("alt", "Icon for Test Article Title");
});
it("handles long titles gracefully", () => {
const longTitlePost = {
...mockPost,
frontmatter: {
...mockPost.frontmatter,
title:
"This is a very long article title that should test how the component handles lengthy text content",
},
};
render(<ContentContainer post={longTitlePost} />);
expect(
screen.getByText(/This is a very long article title/),
).toBeInTheDocument();
});
it("handles long descriptions gracefully", () => {
const longDescPost = {
...mockPost,
frontmatter: {
...mockPost.frontmatter,
description:
"This is a very long article description that should test how the component handles lengthy text content and ensures proper wrapping and truncation behavior.",
},
};
render(<ContentContainer post={longDescPost} />);
expect(
screen.getByText(/This is a very long article description/),
).toBeInTheDocument();
});
});