Files
community-rule/tests/unit/ContentContainer.test.jsx
T
adilalloandCursor c180e79c38 Give every public route its own description, and keep canonicals and structured data on the host that serves the page.
Decorative images on the marketing pages stay quiet, and every dialog close button is named Close.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-04 11:04:52 -07:00

283 lines
8.6 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
function decorativeIcon() {
const icon = document.querySelector("img");
expect(icon).toBeTruthy();
expect(icon).toHaveAttribute("alt", "");
return icon;
}
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 = decorativeIcon();
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-grotesque",
"font-medium",
"text-xx-small-display",
"text-[var(--color-content-invert-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(
"text-x-small-paragraph",
"text-[var(--color-content-invert-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="responsive" />);
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="responsive" />);
const container = document.querySelector("div[class*='relative z-20']");
expect(container).toHaveStyle("width: 200px");
});
it("applies full width and case-study type scale for useCase size", () => {
render(<ContentContainer post={mockPost} size="useCase" />);
const container = document.querySelector("div[class*='relative z-20']");
expect(container).toHaveStyle("width: 100%");
const title = screen.getByText("Test Article Title");
expect(title).toHaveClass("text-[32px]", "lg:text-medium-display");
expect(title).not.toHaveClass("xl:text-x-large-display");
});
it("has proper spacing between icon and text", () => {
render(<ContentContainer post={mockPost} />);
const iconContainer = decorativeIcon().closest("div");
const textContainer = screen.getByText("Test Article Title").closest("div");
// Check the content container (parent of icon)
expect(iconContainer.parentElement).toHaveClass(
"gap-[var(--space-200)]",
);
// 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(--space-200)]",
);
});
it("applies correct metadata text styling", () => {
render(<ContentContainer post={mockPost} />);
const author = screen.getByText("Test Author");
expect(author).toHaveClass(
"text-xx-small-paragraph",
"text-[var(--color-content-invert-brand-royal)]",
);
const date = screen.getByText("April 2025");
expect(date).toHaveClass(
"text-xx-small-paragraph",
"text-[var(--color-content-invert-brand-royal)]",
);
});
it("uses per-article tag assets for catalog slugs", () => {
const { rerender } = render(<ContentContainer post={mockPost} />);
let icon = decorativeIcon();
expect(icon).toHaveAttribute(
"src",
"/content/blog/resolving-active-conflicts-tag.svg",
);
const post2 = { ...mockPost, slug: "operational-security-mutual-aid" };
rerender(<ContentContainer post={post2} />);
icon = decorativeIcon();
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 = decorativeIcon();
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 = decorativeIcon();
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-x-small-paragraph");
});
it("applies correct responsive sizing for responsive breakpoint", () => {
render(<ContentContainer post={mockPost} size="responsive" />);
const icon = decorativeIcon();
expect(icon).toHaveClass("w-[60px]", "h-[30px]");
const title = screen.getByText("Test Article Title");
expect(title).toHaveClass("text-xx-small-display");
const description = screen.getByText(/This is a test article description/);
expect(description).toHaveClass("text-x-small-paragraph");
});
it("has proper accessibility attributes", () => {
render(<ContentContainer post={mockPost} />);
const icon = decorativeIcon();
expect(icon).toHaveAttribute("alt", "");
});
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();
});
});