From 6319d549df3377b74aae05b930cbf0a4547aed83 Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Fri, 12 Sep 2025 10:44:45 -0600 Subject: [PATCH] Add integration tests for content --- .../integration/BlogCore.integration.test.jsx | 171 ++++++++++++++ .../ContentProcessing.integration.test.js | 98 ++++++++ .../RelatedArticles.integration.test.jsx | 214 ++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 tests/integration/BlogCore.integration.test.jsx create mode 100644 tests/integration/ContentProcessing.integration.test.js create mode 100644 tests/integration/RelatedArticles.integration.test.jsx diff --git a/tests/integration/BlogCore.integration.test.jsx b/tests/integration/BlogCore.integration.test.jsx new file mode 100644 index 0000000..175b594 --- /dev/null +++ b/tests/integration/BlogCore.integration.test.jsx @@ -0,0 +1,171 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { render, screen } from "@testing-library/react"; +import RelatedArticles from "../../app/components/RelatedArticles"; + +// Mock ContentThumbnailTemplate with a simple implementation +vi.mock("../../app/components/ContentThumbnailTemplate", () => ({ + default: ({ post, variant }) => ( +
+ +

{post.frontmatter?.title || "Untitled"}

+

{post.frontmatter?.description || "No description"}

+
+
+ ), +})); + +// Mock blog post data +const mockRelatedPosts = [ + { + slug: "resolving-active-conflicts", + frontmatter: { + title: "Resolving Active Conflicts", + description: + "Practical steps for resolving conflicts while maintaining trust", + author: "Test Author", + date: "2025-04-15", + }, + }, + { + slug: "operational-security-mutual-aid", + frontmatter: { + title: "Operational Security for Mutual Aid", + description: + "Tactics to protect members, secure communication, and prevent infiltration", + author: "Test Author", + date: "2025-04-14", + }, + }, + { + slug: "making-decisions-without-hierarchy", + frontmatter: { + title: "Making Decisions Without Hierarchy", + description: + "A brief guide to collaborative nonhierarchical decision making", + author: "Test Author", + date: "2025-04-13", + }, + }, +]; + +describe("Blog Core Integration", () => { + beforeEach(() => { + // Mock window.innerWidth for responsive tests + Object.defineProperty(window, "innerWidth", { + writable: true, + configurable: true, + value: 1024, // Desktop width + }); + }); + + it("should render RelatedArticles component with correct structure", () => { + render( + + ); + + // Verify the section exists + expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent( + "Related Articles" + ); + + // Verify thumbnails are rendered + expect( + screen.getByTestId("thumbnail-operational-security-mutual-aid") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-making-decisions-without-hierarchy") + ).toBeInTheDocument(); + + // Current post should not be displayed + expect( + screen.queryByTestId("thumbnail-resolving-active-conflicts") + ).not.toBeInTheDocument(); + }); + + it("should filter out current post from related articles", () => { + render( + + ); + + // Current post should not be displayed + expect( + screen.queryByTestId("thumbnail-resolving-active-conflicts") + ).not.toBeInTheDocument(); + + // Other posts should be displayed + expect( + screen.getByTestId("thumbnail-operational-security-mutual-aid") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-making-decisions-without-hierarchy") + ).toBeInTheDocument(); + }); + + it("should display all posts when no current post is specified", () => { + render(); + + // All posts should be displayed + expect( + screen.getByTestId("thumbnail-resolving-active-conflicts") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-operational-security-mutual-aid") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-making-decisions-without-hierarchy") + ).toBeInTheDocument(); + }); + + it("should handle empty related posts array", () => { + const { container } = render( + + ); + + expect(container.firstChild).toBeNull(); + }); + + it("should create correct links for each thumbnail", () => { + render( + + ); + + // Verify links are created correctly + const operationalLink = screen + .getByTestId("thumbnail-operational-security-mutual-aid") + .querySelector("a"); + const hierarchyLink = screen + .getByTestId("thumbnail-making-decisions-without-hierarchy") + .querySelector("a"); + + expect(operationalLink).toHaveAttribute( + "href", + "/blog/operational-security-mutual-aid" + ); + expect(hierarchyLink).toHaveAttribute( + "href", + "/blog/making-decisions-without-hierarchy" + ); + }); + + it("should display section heading", () => { + render( + + ); + + expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent( + "Related Articles" + ); + }); +}); diff --git a/tests/integration/ContentProcessing.integration.test.js b/tests/integration/ContentProcessing.integration.test.js new file mode 100644 index 0000000..ef52701 --- /dev/null +++ b/tests/integration/ContentProcessing.integration.test.js @@ -0,0 +1,98 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import fs from "fs"; +import path from "path"; + +// Mock fs and path modules +vi.mock("fs"); +vi.mock("path"); + +// Import the content processing functions +import { getBlogPostFiles, markdownToHtml } from "../../lib/content"; + +describe("Content Processing Integration", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("File System Integration", () => { + it("should read blog post files from content directory", () => { + const mockFiles = ["post1.md", "post2.md", "image.png", "post3.md"]; + fs.readdirSync.mockReturnValue(mockFiles); + + const result = getBlogPostFiles(); + + expect(fs.readdirSync).toHaveBeenCalledWith( + path.join(process.cwd(), "content/blog") + ); + expect(result).toEqual(["post1.md", "post2.md", "post3.md"]); + }); + + it("should handle directory read errors gracefully", () => { + fs.readdirSync.mockImplementation(() => { + throw new Error("Directory not found"); + }); + + const result = getBlogPostFiles(); + + expect(result).toEqual([]); + }); + + it("should filter out non-markdown files", () => { + const mockFiles = [ + "post1.md", + "post2.mdx", + "image.png", + "post3.md", + "readme.txt", + ]; + fs.readdirSync.mockReturnValue(mockFiles); + + const result = getBlogPostFiles(); + + expect(result).toEqual(["post1.md", "post2.mdx", "post3.md"]); + }); + }); + + describe("Markdown to HTML Integration", () => { + it("should convert markdown to HTML with proper formatting", () => { + const markdown = `# Main Title + +## Subtitle + +This is a paragraph with **bold** and *italic* text. + +- List item 1 +- List item 2 + +[Link text](https://example.com)`; + + const result = markdownToHtml(markdown); + + expect(result).toContain("

Main Title

"); + expect(result).toContain("

Subtitle

"); + expect(result).toContain("bold"); + expect(result).toContain("italic"); + expect(result).toContain('Link text'); + }); + + it("should handle empty markdown gracefully", () => { + const result = markdownToHtml(""); + + expect(result).toBe(""); + }); + + it("should handle markdown with special characters", () => { + const markdown = `# Title with Special Characters: & < > " ' + +Content with **bold** and *italic* text.`; + + const result = markdownToHtml(markdown); + + expect(result).toContain( + "

Title with Special Characters: & < > \" '

" + ); + expect(result).toContain("bold"); + expect(result).toContain("italic"); + }); + }); +}); diff --git a/tests/integration/RelatedArticles.integration.test.jsx b/tests/integration/RelatedArticles.integration.test.jsx new file mode 100644 index 0000000..e4678a3 --- /dev/null +++ b/tests/integration/RelatedArticles.integration.test.jsx @@ -0,0 +1,214 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import RelatedArticles from "../../app/components/RelatedArticles"; + +// Mock ContentThumbnailTemplate +vi.mock("../../app/components/ContentThumbnailTemplate", () => ({ + default: ({ post, variant }) => ( + + ), +})); + +// Mock blog post data +const mockRelatedPosts = [ + { + slug: "resolving-active-conflicts", + frontmatter: { + title: "Resolving Active Conflicts", + description: + "Practical steps for resolving conflicts while maintaining trust", + author: "Test Author", + date: "2025-04-15", + }, + }, + { + slug: "operational-security-mutual-aid", + frontmatter: { + title: "Operational Security for Mutual Aid", + description: + "Tactics to protect members, secure communication, and prevent infiltration", + author: "Test Author", + date: "2025-04-14", + }, + }, + { + slug: "making-decisions-without-hierarchy", + frontmatter: { + title: "Making Decisions Without Hierarchy", + description: + "A brief guide to collaborative nonhierarchical decision making", + author: "Test Author", + date: "2025-04-13", + }, + }, + { + slug: "building-community-trust", + frontmatter: { + title: "Building Community Trust", + description: "Strategies for fostering trust in community organizations", + author: "Test Author", + date: "2025-04-12", + }, + }, +]; + +describe("Related Articles Integration", () => { + beforeEach(() => { + // Mock window.innerWidth for responsive tests + Object.defineProperty(window, "innerWidth", { + writable: true, + configurable: true, + value: 1024, // Desktop width + }); + }); + + it("should filter out current post from related articles", () => { + render( + + ); + + // Current post should not be displayed + expect( + screen.queryByTestId("thumbnail-resolving-active-conflicts") + ).not.toBeInTheDocument(); + + // Other posts should be displayed + expect( + screen.getByTestId("thumbnail-operational-security-mutual-aid") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-making-decisions-without-hierarchy") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-building-community-trust") + ).toBeInTheDocument(); + }); + + it("should display all posts when no current post is specified", () => { + render(); + + // All posts should be displayed + expect( + screen.getByTestId("thumbnail-resolving-active-conflicts") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-operational-security-mutual-aid") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-making-decisions-without-hierarchy") + ).toBeInTheDocument(); + expect( + screen.getByTestId("thumbnail-building-community-trust") + ).toBeInTheDocument(); + }); + + it("should create correct links for each thumbnail", () => { + render( + + ); + + // Verify links are created correctly + expect( + screen.getByTestId("thumbnail-link-operational-security-mutual-aid") + ).toHaveAttribute("href", "/blog/operational-security-mutual-aid"); + expect( + screen.getByTestId("thumbnail-link-making-decisions-without-hierarchy") + ).toHaveAttribute("href", "/blog/making-decisions-without-hierarchy"); + expect( + screen.getByTestId("thumbnail-link-building-community-trust") + ).toHaveAttribute("href", "/blog/building-community-trust"); + }); + + it("should handle empty related posts array", () => { + const { container } = render( + + ); + + expect(container.firstChild).toBeNull(); + }); + + it("should handle single related post", () => { + const singlePost = [mockRelatedPosts[0]]; + + render( + + ); + + expect( + screen.getByTestId("thumbnail-resolving-active-conflicts") + ).toBeInTheDocument(); + expect( + screen.queryByTestId("thumbnail-operational-security-mutual-aid") + ).not.toBeInTheDocument(); + }); + + it("should handle all posts being filtered out", () => { + const currentPostOnly = [mockRelatedPosts[0]]; + + const { container } = render( + + ); + + expect(container.firstChild).toBeNull(); + }); + + it("should display section heading", () => { + render( + + ); + + expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent( + "Related Articles" + ); + }); + + it("should maintain consistent structure across different current posts", () => { + const slugs = [ + "resolving-active-conflicts", + "operational-security-mutual-aid", + "making-decisions-without-hierarchy", + ]; + + slugs.forEach((slug) => { + const { unmount } = render( + + ); + + // Verify consistent structure + expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent( + "Related Articles" + ); + // Check that we have some thumbnails (the exact ones depend on the current post) + const thumbnails = screen.getAllByTestId(/thumbnail-/); + expect(thumbnails.length).toBeGreaterThan(0); + + unmount(); + }); + }); +});