Update learn page

This commit is contained in:
adilallo
2026-05-20 22:17:00 -06:00
parent 7ee6282c1a
commit 1688ac85c9
45 changed files with 1203 additions and 350 deletions
+113
View File
@@ -0,0 +1,113 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { screen, within } from "@testing-library/react";
import { renderWithProviders as render } from "../utils/test-utils";
import LearnPage from "../../app/(marketing)/learn/page";
vi.mock("../../lib/content", () => ({
getAllBlogPosts: vi.fn(),
}));
vi.mock("../../app/components/sections/AskOrganizer", () => ({
default: ({
title,
subtitle,
buttonText,
}: {
title: string;
subtitle: string;
buttonText: string;
}) => (
<div data-testid="ask-organizer">
<h2>{title}</h2>
<p>{subtitle}</p>
<button type="button">{buttonText}</button>
</div>
),
}));
const mockPosts = [
{
slug: "resolving-active-conflicts",
frontmatter: {
title: "Resolving Active Conflicts",
description: "Practical steps for resolving conflicts",
author: "Author name",
date: "2025-04-15",
thumbnail: {
vertical: "resolving-active-conflicts-vertical.svg",
horizontal: "resolving-active-conflicts-horizontal.svg",
},
},
content: "",
htmlContent: "",
filePath: "resolving-active-conflicts.md",
lastModified: new Date(),
},
{
slug: "operational-security-mutual-aid",
frontmatter: {
title: "Operational Security for Mutual Aid",
description: "Tactics to protect members",
author: "Author name",
date: "2025-04-10",
thumbnail: {
vertical: "operational-security-mutual-aid-vertical.svg",
horizontal: "operational-security-mutual-aid-horizontal.svg",
},
},
content: "",
htmlContent: "",
filePath: "operational-security-mutual-aid.md",
lastModified: new Date(),
},
];
describe("LearnPage", () => {
beforeEach(async () => {
vi.clearAllMocks();
const { getAllBlogPosts } = await import("../../lib/content");
vi.mocked(getAllBlogPosts).mockReturnValue(mockPosts);
});
it("renders content lockup and ask organizer copy", () => {
render(<LearnPage />);
expect(screen.getByText("Organizing is hard")).toBeInTheDocument();
expect(
screen.getByText(
/Find answers to your questions and see how other groups/,
),
).toBeInTheDocument();
expect(screen.getByTestId("ask-organizer")).toBeInTheDocument();
expect(screen.getByText("Still have questions?")).toBeInTheDocument();
});
it("renders one card per post in each layout region without duplication", () => {
const { container } = render(<LearnPage />);
const mobileRegion = container.querySelector(".smd\\:hidden");
const desktopRegion = container.querySelector(".smd\\:grid");
expect(mobileRegion).toBeTruthy();
expect(desktopRegion).toBeTruthy();
const mobileLinks = within(mobileRegion as HTMLElement).getAllByRole(
"link",
);
const desktopLinks = within(desktopRegion as HTMLElement).getAllByRole(
"link",
);
expect(mobileLinks).toHaveLength(mockPosts.length);
expect(desktopLinks).toHaveLength(mockPosts.length);
expect(mobileLinks[0]).toHaveAttribute(
"href",
"/blog/resolving-active-conflicts",
);
expect(desktopLinks[1]).toHaveAttribute(
"href",
"/blog/operational-security-mutual-aid",
);
});
});
+18 -8
View File
@@ -5,6 +5,12 @@ import ContentContainer from "../../app/components/content/ContentContainer";
// Mock asset utils
vi.mock("../../lib/assetUtils", () => ({
getAssetPath: vi.fn((asset) => `/assets/${asset}`),
contentBlogTagPath: vi.fn((slug) => `/content/blog/${slug}-tag.svg`),
CONTENT_CATALOG_SLUG_ORDER: [
"resolving-active-conflicts",
"operational-security-mutual-aid",
"making-decisions-without-hierarchy",
],
ASSETS: {
ICON_1: "Icon_1.svg",
ICON_2: "Icon_2.svg",
@@ -121,7 +127,8 @@ describe("ContentContainer", () => {
const metadataContainer = screen.getByText("Test Author").closest("div");
expect(metadataContainer).toHaveClass(
"flex",
"items-center",
"min-w-0",
"items-end",
"gap-[var(--measures-spacing-008)]",
);
});
@@ -148,26 +155,29 @@ describe("ContentContainer", () => {
);
});
it("cycles through different icons based on slug", () => {
it("uses per-article tag assets for catalog slugs", () => {
const { rerender } = render(<ContentContainer post={mockPost} />);
// First render should use Icon_1
let icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveAttribute("src", "/assets/Icon_1.svg");
// Test with different slug
const post2 = { ...mockPost, slug: "operational-security-mutual-aid" };
rerender(<ContentContainer post={post2} />);
icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveAttribute("src", "/assets/Icon_2.svg");
expect(icon).toHaveAttribute(
"src",
"/content/blog/operational-security-mutual-aid-tag.svg",
);
// Test with another slug
const post3 = { ...mockPost, slug: "making-decisions-without-hierarchy" };
rerender(<ContentContainer post={post3} />);
icon = screen.getByAltText("Icon for Test Article Title");
expect(icon).toHaveAttribute("src", "/assets/Icon_3.svg");
expect(icon).toHaveAttribute(
"src",
"/content/blog/making-decisions-without-hierarchy-tag.svg",
);
});
it("handles missing post data gracefully", () => {
@@ -191,7 +201,7 @@ describe("ContentContainer", () => {
expect(icon).toHaveClass("w-[60px]", "h-[30px]");
const title = screen.getByText("Test Article Title");
expect(title).toHaveClass("text-[18px]", "leading-[120%]");
expect(title).toHaveClass("text-[18px]", "leading-[22px]");
const description = screen.getByText(/This is a test article description/);
expect(description).toHaveClass("text-[12px]", "leading-[16px]");
+19 -91
View File
@@ -2,7 +2,6 @@ import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import ContentThumbnailTemplate from "../../app/components/content/ContentThumbnailTemplate";
// Mock Next.js components
vi.mock("next/link", () => {
return {
default: ({ children, href, ...props }) => (
@@ -13,13 +12,6 @@ vi.mock("next/link", () => {
};
});
vi.mock("next/image", () => {
return {
default: ({ src, alt, ...props }) => <img src={src} alt={alt} {...props} />,
};
});
// Mock blog post data
const mockPost = {
slug: "test-post",
frontmatter: {
@@ -35,18 +27,14 @@ const mockPost = {
},
};
// Pure presentational; no provider context needed.
describe("ContentThumbnailTemplate", () => {
describe("Vertical Variant", () => {
it("should render vertical variant with responsive dimensions", () => {
it("should render vertical variant with fluid Figma aspect ratio", () => {
render(<ContentThumbnailTemplate post={mockPost} />);
const container = screen.getByRole("link");
expect(container).toBeInTheDocument();
// Check that the component has the correct classes for responsive dimensions
const thumbnailDiv = container.querySelector("div");
expect(thumbnailDiv).toHaveClass("w-full", "aspect-[2/3]");
const thumbnailDiv = container.querySelector("div.relative");
expect(thumbnailDiv).toHaveClass("aspect-[260/390]", "w-full");
});
it("should display post title and description", () => {
@@ -67,29 +55,20 @@ describe("ContentThumbnailTemplate", () => {
});
describe("Horizontal Variant", () => {
it("should render horizontal variant with responsive sizing", () => {
it("should render horizontal variant with fluid Figma aspect ratio", () => {
render(<ContentThumbnailTemplate post={mockPost} variant="horizontal" />);
const container = screen.getByRole("link");
expect(container).toBeInTheDocument();
// Check that the component has the correct classes for horizontal layout
const thumbnailDiv = container.querySelector("div");
expect(thumbnailDiv).toHaveClass(
"min-w-[320px]",
"max-w-[800px]",
"h-[225.5px]",
);
const thumbnailDiv = container.querySelector("div.relative");
expect(thumbnailDiv).toHaveClass("aspect-[320/225.5]", "w-full");
});
it("should display post information in horizontal layout", () => {
render(<ContentThumbnailTemplate post={mockPost} />);
it("should render fixed vertical dimensions when sizing is fixed", () => {
render(<ContentThumbnailTemplate post={mockPost} sizing="fixed" />);
expect(screen.getByText("Test Blog Post Title")).toBeInTheDocument();
expect(
screen.getByText(/This is a test description/),
).toBeInTheDocument();
expect(screen.getByText("Test Author")).toBeInTheDocument();
const container = screen.getByRole("link");
const thumbnailDiv = container.querySelector("div.relative");
expect(thumbnailDiv).toHaveClass("h-[390px]", "w-[260px]");
});
});
@@ -99,83 +78,32 @@ describe("ContentThumbnailTemplate", () => {
<ContentThumbnailTemplate post={mockPost} className="custom-class" />,
);
const container = screen.getByRole("link");
expect(container).toHaveClass("custom-class");
expect(screen.getByRole("link")).toHaveClass("custom-class");
});
it("should generate correct link href", () => {
render(<ContentThumbnailTemplate post={mockPost} />);
const link = screen.getByRole("link");
expect(link).toHaveAttribute("href", "/blog/test-post");
});
it("should handle posts without tags gracefully", () => {
const postWithoutTags = {
...mockPost,
frontmatter: {
...mockPost.frontmatter,
tags: [],
},
};
render(<ContentThumbnailTemplate post={postWithoutTags} />);
// Should still render without errors
expect(screen.getByText("Test Blog Post Title")).toBeInTheDocument();
});
it("should handle posts without thumbnail images", () => {
const postWithoutImages = {
...mockPost,
frontmatter: {
...mockPost.frontmatter,
thumbnail: undefined,
},
};
render(<ContentThumbnailTemplate post={postWithoutImages} />);
// Should still render without errors
expect(screen.getByText("Test Blog Post Title")).toBeInTheDocument();
expect(screen.getByRole("link")).toHaveAttribute(
"href",
"/blog/test-post",
);
});
it("should use article-specific thumbnail images when provided", () => {
render(<ContentThumbnailTemplate post={mockPost} />);
// Check that the background image uses the article-specific thumbnail
const backgroundImg = document.querySelector(
"img[alt*='Background for']",
);
const backgroundImg = document.querySelector('img[src*="test-post-vertical"]');
expect(backgroundImg).toBeInTheDocument();
expect(backgroundImg.src).toContain("test-post-vertical.svg");
});
it("should use article-specific horizontal images for horizontal variant", () => {
it("should use horizontal thumbnail for horizontal variant", () => {
render(<ContentThumbnailTemplate post={mockPost} variant="horizontal" />);
// Check that the background image uses the article-specific horizontal thumbnail
const backgroundImg = document.querySelector(
"img[alt*='Background for']",
'img[src*="test-post-horizontal"]',
);
expect(backgroundImg).toBeInTheDocument();
expect(backgroundImg.src).toContain("test-post-horizontal.svg");
});
});
describe("Default Behavior", () => {
it("should default to vertical variant when no variant specified", () => {
render(<ContentThumbnailTemplate post={mockPost} />);
const thumbnailDiv = screen.getByRole("link").querySelector("div");
expect(thumbnailDiv).toHaveClass("w-full", "aspect-[2/3]");
});
it("should show metadata by default", () => {
render(<ContentThumbnailTemplate post={mockPost} />);
expect(screen.getByText("Test Author")).toBeInTheDocument();
expect(screen.getByText("April 2025")).toBeInTheDocument();
});
});
});