Run lint and prettier
CI Pipeline / test (20) (pull_request) Failing after 8m5s
CI Pipeline / test (18) (pull_request) Failing after 8m36s
CI Pipeline / e2e (chromium) (pull_request) Successful in 2m32s
CI Pipeline / e2e (webkit) (pull_request) Successful in 3m40s
CI Pipeline / e2e (firefox) (pull_request) Successful in 5m43s
CI Pipeline / performance (pull_request) Failing after 3m18s
CI Pipeline / visual-regression (pull_request) Failing after 3m20s
CI Pipeline / lint (pull_request) Successful in 1m7s
CI Pipeline / storybook (pull_request) Successful in 1m32s
CI Pipeline / build (pull_request) Successful in 1m22s
CI Pipeline / test (20) (pull_request) Failing after 8m5s
CI Pipeline / test (18) (pull_request) Failing after 8m36s
CI Pipeline / e2e (chromium) (pull_request) Successful in 2m32s
CI Pipeline / e2e (webkit) (pull_request) Successful in 3m40s
CI Pipeline / e2e (firefox) (pull_request) Successful in 5m43s
CI Pipeline / performance (pull_request) Failing after 3m18s
CI Pipeline / visual-regression (pull_request) Failing after 3m20s
CI Pipeline / lint (pull_request) Successful in 1m7s
CI Pipeline / storybook (pull_request) Successful in 1m32s
CI Pipeline / build (pull_request) Successful in 1m22s
This commit is contained in:
@@ -20,7 +20,7 @@ export default function ContentBanner({ post }) {
|
||||
className="absolute inset-0 w-full h-full bg-cover bg-no-repeat aspect-[640/224] md:block hidden"
|
||||
style={{
|
||||
backgroundImage: `url(${getAssetPath(
|
||||
"assets/Content_Banner_2.svg"
|
||||
"assets/Content_Banner_2.svg",
|
||||
)})`,
|
||||
backgroundPosition: "center bottom",
|
||||
}}
|
||||
|
||||
+20
-20
@@ -94,26 +94,26 @@ export default function Logo({ size = "default", showText = true }) {
|
||||
size === "homeHeaderXsmall"
|
||||
? sizes.homeHeaderXsmall
|
||||
: size === "homeHeaderSm"
|
||||
? sizes.homeHeaderSm
|
||||
: size === "homeHeaderMd"
|
||||
? sizes.homeHeaderMd
|
||||
: size === "homeHeaderLg"
|
||||
? sizes.homeHeaderLg
|
||||
: size === "homeHeaderXl"
|
||||
? sizes.homeHeaderXl
|
||||
: size === "header"
|
||||
? sizes.header
|
||||
: size === "headerMd"
|
||||
? sizes.headerMd
|
||||
: size === "headerLg"
|
||||
? sizes.headerLg
|
||||
: size === "headerXl"
|
||||
? sizes.headerXl
|
||||
: size === "footer"
|
||||
? sizes.footer
|
||||
: size === "footerLg"
|
||||
? sizes.footerLg
|
||||
: sizes.default;
|
||||
? sizes.homeHeaderSm
|
||||
: size === "homeHeaderMd"
|
||||
? sizes.homeHeaderMd
|
||||
: size === "homeHeaderLg"
|
||||
? sizes.homeHeaderLg
|
||||
: size === "homeHeaderXl"
|
||||
? sizes.homeHeaderXl
|
||||
: size === "header"
|
||||
? sizes.header
|
||||
: size === "headerMd"
|
||||
? sizes.headerMd
|
||||
: size === "headerLg"
|
||||
? sizes.headerLg
|
||||
: size === "headerXl"
|
||||
? sizes.headerXl
|
||||
: size === "footer"
|
||||
? sizes.footer
|
||||
: size === "footerLg"
|
||||
? sizes.footerLg
|
||||
: sizes.default;
|
||||
|
||||
return (
|
||||
<Link href="/" className="block" aria-label="CommunityRule Logo">
|
||||
|
||||
@@ -6,7 +6,7 @@ import ContentThumbnailTemplate from "./ContentThumbnailTemplate";
|
||||
export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
||||
// Filter out the current post from related posts
|
||||
const filteredPosts = relatedPosts.filter(
|
||||
(post) => post.slug !== currentPostSlug
|
||||
(post) => post.slug !== currentPostSlug,
|
||||
);
|
||||
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
@@ -93,7 +93,7 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
||||
const handleMouseUp = () => {
|
||||
document.removeEventListener(
|
||||
"mousemove",
|
||||
handleMouseMove
|
||||
handleMouseMove,
|
||||
);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
@@ -133,8 +133,8 @@ export default function RelatedArticles({ relatedPosts, currentPostSlug }) {
|
||||
index === currentIndex
|
||||
? `${progress}%`
|
||||
: index < currentIndex
|
||||
? "100%"
|
||||
: "0%",
|
||||
? "100%"
|
||||
: "0%",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+8
-5
@@ -29,12 +29,15 @@
|
||||
--color-*: initial;
|
||||
|
||||
/* Font families */
|
||||
--font-sans: var(--font-inter), ui-sans-serif, system-ui, -apple-system,
|
||||
--font-sans:
|
||||
var(--font-inter), ui-sans-serif, system-ui, -apple-system, "Segoe UI",
|
||||
Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-display:
|
||||
var(--font-bricolage-grotesque), ui-sans-serif, system-ui, -apple-system,
|
||||
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-display: var(--font-bricolage-grotesque), ui-sans-serif, system-ui,
|
||||
-apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-mono: var(--font-space-grotesk), ui-monospace, SFMono-Regular,
|
||||
"SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
||||
--font-mono:
|
||||
var(--font-space-grotesk), ui-monospace, SFMono-Regular, "SF Mono",
|
||||
Consolas, "Liberation Mono", Menlo, monospace;
|
||||
|
||||
/* Dimension */
|
||||
--spacing-scale-000: 0px;
|
||||
|
||||
@@ -92,8 +92,8 @@ export default function TestThumbnailPage() {
|
||||
<code>className</code> - Additional CSS classes
|
||||
</li>
|
||||
<li>
|
||||
<code>variant</code> - "vertical" (default) or "horizontal"
|
||||
(for development/testing)
|
||||
<code>variant</code> - "vertical" (default) or
|
||||
"horizontal" (for development/testing)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+10
-10
@@ -60,7 +60,7 @@ export function getBlogPostFiles() {
|
||||
try {
|
||||
const files = fs.readdirSync(contentDirectory);
|
||||
return files.filter(
|
||||
(file) => file.endsWith(".md") || file.endsWith(".mdx")
|
||||
(file) => file.endsWith(".md") || file.endsWith(".mdx"),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error reading blog content directory:", error);
|
||||
@@ -84,7 +84,7 @@ export function parseBlogPost(filePath) {
|
||||
if (!validationResult.isValid) {
|
||||
console.error(
|
||||
`Validation errors for ${filePath}:`,
|
||||
validationResult.errors
|
||||
validationResult.errors,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -116,7 +116,7 @@ export function getAllBlogPosts() {
|
||||
.map((fileName) => parseBlogPost(fileName))
|
||||
.filter(Boolean) // Filter out nulls (invalid posts)
|
||||
.sort(
|
||||
(a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date)
|
||||
(a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date),
|
||||
); // Sort by date descending
|
||||
return allPosts;
|
||||
}
|
||||
@@ -141,11 +141,11 @@ export function getBlogPostBySlug(slug) {
|
||||
export function getRelatedBlogPosts(
|
||||
currentPostSlug,
|
||||
relatedSlugs = [],
|
||||
limit = 3
|
||||
limit = 3,
|
||||
) {
|
||||
const allPosts = getAllBlogPosts();
|
||||
const filteredPosts = allPosts.filter(
|
||||
(post) => post.slug !== currentPostSlug
|
||||
(post) => post.slug !== currentPostSlug,
|
||||
);
|
||||
|
||||
let related = [];
|
||||
@@ -191,7 +191,7 @@ export function getAllTags() {
|
||||
export function getBlogPostsByTag(tag) {
|
||||
const allPosts = getAllBlogPosts();
|
||||
return allPosts.filter(
|
||||
(post) => post.frontmatter.tags && post.frontmatter.tags.includes(tag)
|
||||
(post) => post.frontmatter.tags && post.frontmatter.tags.includes(tag),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ export function searchBlogPosts(query, limit = 10) {
|
||||
.includes(searchTerm);
|
||||
const contentMatch = post.content.toLowerCase().includes(searchTerm);
|
||||
const tagMatch = post.frontmatter.tags?.some((tag) =>
|
||||
tag.toLowerCase().includes(searchTerm)
|
||||
tag.toLowerCase().includes(searchTerm),
|
||||
);
|
||||
|
||||
return titleMatch || descriptionMatch || contentMatch || tagMatch;
|
||||
@@ -233,7 +233,7 @@ export function searchBlogPosts(query, limit = 10) {
|
||||
export function getBlogPostsByAuthor(author) {
|
||||
const allPosts = getAllBlogPosts();
|
||||
return allPosts.filter(
|
||||
(post) => post.frontmatter.author.toLowerCase() === author.toLowerCase()
|
||||
(post) => post.frontmatter.author.toLowerCase() === author.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -274,9 +274,9 @@ export function getBlogStats() {
|
||||
1,
|
||||
(new Date(allPosts[0].frontmatter.date) -
|
||||
new Date(allPosts[allPosts.length - 1].frontmatter.date)) /
|
||||
(1000 * 60 * 60 * 24 * 30)
|
||||
(1000 * 60 * 60 * 24 * 30),
|
||||
)) *
|
||||
10
|
||||
10,
|
||||
) / 10
|
||||
: 0,
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ class ContentProcessor {
|
||||
// Warm up cache
|
||||
await warmCache(
|
||||
() => this.getAllPosts(),
|
||||
() => this.getAllTags()
|
||||
() => this.getAllTags(),
|
||||
);
|
||||
|
||||
this.isInitialized = true;
|
||||
@@ -60,7 +60,7 @@ class ContentProcessor {
|
||||
try {
|
||||
const files = fs.readdirSync(this.contentDirectory);
|
||||
return files.filter(
|
||||
(file) => file.endsWith(".md") || file.endsWith(".mdx")
|
||||
(file) => file.endsWith(".md") || file.endsWith(".mdx"),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error reading blog content directory:", error);
|
||||
@@ -85,7 +85,7 @@ class ContentProcessor {
|
||||
if (!validationResult.isValid) {
|
||||
console.error(
|
||||
`Validation errors for ${filePath}:`,
|
||||
validationResult.errors
|
||||
validationResult.errors,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -145,7 +145,7 @@ class ContentProcessor {
|
||||
.map((fileName) => this.processBlogPost(fileName))
|
||||
.filter(Boolean)
|
||||
.sort(
|
||||
(a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date)
|
||||
(a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date),
|
||||
);
|
||||
|
||||
// Cache the result
|
||||
@@ -230,7 +230,7 @@ class ContentProcessor {
|
||||
return {
|
||||
totalPosts: allPosts.length,
|
||||
totalAuthors: new Set(
|
||||
allPosts.map((post) => post.frontmatter.author).size
|
||||
allPosts.map((post) => post.frontmatter.author).size,
|
||||
),
|
||||
dateRange: {
|
||||
earliest:
|
||||
@@ -247,9 +247,9 @@ class ContentProcessor {
|
||||
1,
|
||||
(new Date(allPosts[0].frontmatter.date) -
|
||||
new Date(allPosts[allPosts.length - 1].frontmatter.date)) /
|
||||
(1000 * 60 * 60 * 24 * 30)
|
||||
(1000 * 60 * 60 * 24 * 30),
|
||||
)) *
|
||||
10
|
||||
10,
|
||||
) / 10
|
||||
: 0,
|
||||
};
|
||||
|
||||
+13
-13
@@ -159,34 +159,34 @@ function markdownToHtml(markdown) {
|
||||
// Headers with IDs
|
||||
.replace(
|
||||
/^###### (.*$)/gim,
|
||||
(m, t) => `<h6 id="${generateHeadingId(t)}">${t}</h6>`
|
||||
(m, t) => `<h6 id="${generateHeadingId(t)}">${t}</h6>`,
|
||||
)
|
||||
.replace(
|
||||
/^##### (.*$)/gim,
|
||||
(m, t) => `<h5 id="${generateHeadingId(t)}">${t}</h5>`
|
||||
(m, t) => `<h5 id="${generateHeadingId(t)}">${t}</h5>`,
|
||||
)
|
||||
.replace(
|
||||
/^#### (.*$)/gim,
|
||||
(m, t) => `<h4 id="${generateHeadingId(t)}">${t}</h4>`
|
||||
(m, t) => `<h4 id="${generateHeadingId(t)}">${t}</h4>`,
|
||||
)
|
||||
.replace(
|
||||
/^### (.*$)/gim,
|
||||
(m, t) => `<h3 id="${generateHeadingId(t)}">${t}</h3>`
|
||||
(m, t) => `<h3 id="${generateHeadingId(t)}">${t}</h3>`,
|
||||
)
|
||||
.replace(
|
||||
/^## (.*$)/gim,
|
||||
(m, t) => `<h2 id="${generateHeadingId(t)}">${t}</h2>`
|
||||
(m, t) => `<h2 id="${generateHeadingId(t)}">${t}</h2>`,
|
||||
)
|
||||
.replace(
|
||||
/^# (.*$)/gim,
|
||||
(m, t) => `<h1 id="${generateHeadingId(t)}">${t}</h1>`
|
||||
(m, t) => `<h1 id="${generateHeadingId(t)}">${t}</h1>`,
|
||||
)
|
||||
|
||||
// Code fences (block) and inline code
|
||||
.replace(
|
||||
/```(\w+)?\n([\s\S]*?)\n```/g,
|
||||
(m, lang = "", code) =>
|
||||
`<pre><code class="language-${lang}">${code}</code></pre>`
|
||||
`<pre><code class="language-${lang}">${code}</code></pre>`,
|
||||
)
|
||||
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
||||
|
||||
@@ -198,12 +198,12 @@ function markdownToHtml(markdown) {
|
||||
.replace(
|
||||
/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/g,
|
||||
(m, alt, src, title = "") =>
|
||||
`<img alt="${alt}" src="${src}"${title ? ` title="${title}"` : ""}>`
|
||||
`<img alt="${alt}" src="${src}"${title ? ` title="${title}"` : ""}>`,
|
||||
)
|
||||
.replace(
|
||||
/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/g,
|
||||
(m, text, href, title = "") =>
|
||||
`<a href="${href}"${title ? ` title="${title}"` : ""}>${text}</a>`
|
||||
`<a href="${href}"${title ? ` title="${title}"` : ""}>${text}</a>`,
|
||||
)
|
||||
|
||||
// Blockquotes
|
||||
@@ -211,7 +211,7 @@ function markdownToHtml(markdown) {
|
||||
const inner = m.replace(/^>\s?/gm, "");
|
||||
return `<blockquote><p>${inner.replace(
|
||||
/\n{2,}/g,
|
||||
"</p><p>"
|
||||
"</p><p>",
|
||||
)}</p></blockquote>`;
|
||||
})
|
||||
|
||||
@@ -247,7 +247,7 @@ function markdownToHtml(markdown) {
|
||||
// (Also skip our GAP_TOKEN so we can turn it into gap paragraphs later.)
|
||||
.replace(
|
||||
/^(?!\s*<(h[1-6]|ul|ol|li|blockquote|hr|pre|code|table|img)\b)(?!\s*<\/)(?!\s*<GAP\/>)(.+)$/gim,
|
||||
"<p>$2</p>"
|
||||
"<p>$2</p>",
|
||||
)
|
||||
|
||||
// Clean up truly empty paragraphs but keep gap paragraphs
|
||||
@@ -258,8 +258,8 @@ function markdownToHtml(markdown) {
|
||||
/<GAP:(\d+)\/>/g,
|
||||
(m, n) =>
|
||||
`<div class="md-gap" style="--gap:${Number(
|
||||
n
|
||||
)}" aria-hidden="true"></div>`
|
||||
n,
|
||||
)}" aria-hidden="true"></div>`,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
+4
-4
@@ -81,12 +81,12 @@ export function validateBlogPost(frontmatter) {
|
||||
if (config.type === "string" && typeof frontmatter[field] === "string") {
|
||||
if (config.minLength && frontmatter[field].length < config.minLength) {
|
||||
errors.push(
|
||||
`Field ${field} must be at least ${config.minLength} characters`
|
||||
`Field ${field} must be at least ${config.minLength} characters`,
|
||||
);
|
||||
}
|
||||
if (config.maxLength && frontmatter[field].length > config.maxLength) {
|
||||
errors.push(
|
||||
`Field ${field} must be no more than ${config.maxLength} characters`
|
||||
`Field ${field} must be no more than ${config.maxLength} characters`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -105,12 +105,12 @@ export function validateBlogPost(frontmatter) {
|
||||
}
|
||||
if (config.items.minLength && item.length < config.items.minLength) {
|
||||
errors.push(
|
||||
`Item ${i} in ${field} must be at least ${config.items.minLength} characters`
|
||||
`Item ${i} in ${field} must be at least ${config.items.minLength} characters`,
|
||||
);
|
||||
}
|
||||
if (config.items.maxLength && item.length > config.items.maxLength) {
|
||||
errors.push(
|
||||
`Item ${i} in ${field} must be no more than ${config.items.maxLength} characters`
|
||||
`Item ${i} in ${field} must be no more than ${config.items.maxLength} characters`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ describe("Blog Navigation E2E", () => {
|
||||
expect(thumbnailLink).toBeInTheDocument();
|
||||
expect(thumbnailLink).toHaveAttribute(
|
||||
"href",
|
||||
"/blog/resolving-active-conflicts"
|
||||
"/blog/resolving-active-conflicts",
|
||||
);
|
||||
|
||||
// Click the thumbnail
|
||||
@@ -102,12 +102,12 @@ describe("Blog Navigation E2E", () => {
|
||||
|
||||
// Verify post content is displayed
|
||||
expect(
|
||||
screen.getByText("Resolving Active Conflicts")
|
||||
screen.getByText("Resolving Active Conflicts"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
"Practical steps for resolving conflicts while maintaining trust"
|
||||
)
|
||||
"Practical steps for resolving conflicts while maintaining trust",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Author")).toBeInTheDocument();
|
||||
expect(screen.getByText("April 2025")).toBeInTheDocument();
|
||||
@@ -128,10 +128,10 @@ describe("Blog Navigation E2E", () => {
|
||||
|
||||
// Verify related articles are displayed
|
||||
expect(
|
||||
screen.getByText("Operational Security for Mutual Aid")
|
||||
screen.getByText("Operational Security for Mutual Aid"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Making Decisions Without Hierarchy")
|
||||
screen.getByText("Making Decisions Without Hierarchy"),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Verify links are present
|
||||
@@ -139,11 +139,11 @@ describe("Blog Navigation E2E", () => {
|
||||
expect(relatedLinks).toHaveLength(2);
|
||||
expect(relatedLinks[0]).toHaveAttribute(
|
||||
"href",
|
||||
"/blog/operational-security-mutual-aid"
|
||||
"/blog/operational-security-mutual-aid",
|
||||
);
|
||||
expect(relatedLinks[1]).toHaveAttribute(
|
||||
"href",
|
||||
"/blog/making-decisions-without-hierarchy"
|
||||
"/blog/making-decisions-without-hierarchy",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -160,7 +160,7 @@ describe("Blog Navigation E2E", () => {
|
||||
|
||||
// Verify navigation was called
|
||||
expect(mockPush).toHaveBeenCalledWith(
|
||||
"/blog/operational-security-mutual-aid"
|
||||
"/blog/operational-security-mutual-aid",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -176,7 +176,7 @@ describe("Blog Navigation E2E", () => {
|
||||
it("should complete navigation flow: thumbnail → related article", () => {
|
||||
// Render thumbnail
|
||||
const { rerender } = render(
|
||||
<ContentThumbnailTemplate post={mockBlogPost} />
|
||||
<ContentThumbnailTemplate post={mockBlogPost} />,
|
||||
);
|
||||
|
||||
// Click thumbnail
|
||||
@@ -194,7 +194,7 @@ describe("Blog Navigation E2E", () => {
|
||||
.closest("a");
|
||||
fireEvent.click(relatedLink);
|
||||
expect(mockPush).toHaveBeenCalledWith(
|
||||
"/blog/operational-security-mutual-aid"
|
||||
"/blog/operational-security-mutual-aid",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ describe("Content Page Rendering E2E", () => {
|
||||
// Verify banner content
|
||||
expect(screen.getByText("Test Article Title")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("This is a test article description")
|
||||
screen.getByText("This is a test article description"),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Author")).toBeInTheDocument();
|
||||
expect(screen.getByText("April 2025")).toBeInTheDocument();
|
||||
@@ -112,16 +112,16 @@ describe("Content Page Rendering E2E", () => {
|
||||
title="Still have questions?"
|
||||
subtitle="Get help from our community organizers"
|
||||
description="We're here to help you with any questions or concerns."
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Verify ask organizer content
|
||||
expect(screen.getByText("Still have questions?")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Get help from our community organizers")
|
||||
screen.getByText("Get help from our community organizers"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("link", { name: /ask an organizer/i })
|
||||
screen.getByRole("link", { name: /ask an organizer/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -131,16 +131,16 @@ describe("Content Page Rendering E2E", () => {
|
||||
variant="inverse"
|
||||
title="Still have questions?"
|
||||
subtitle="Get help from our community organizers"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Verify ask organizer content is still present
|
||||
expect(screen.getByText("Still have questions?")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Get help from our community organizers")
|
||||
screen.getByText("Get help from our community organizers"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("link", { name: /ask an organizer/i })
|
||||
screen.getByRole("link", { name: /ask an organizer/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -163,7 +163,7 @@ describe("Content Page Rendering E2E", () => {
|
||||
title="Still have questions?"
|
||||
subtitle="Get help from our community organizers"
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
|
||||
// Verify both components are rendered
|
||||
@@ -179,7 +179,7 @@ describe("Content Page Rendering E2E", () => {
|
||||
title="Still have questions?"
|
||||
subtitle="Get help from our community organizers"
|
||||
/>
|
||||
</main>
|
||||
</main>,
|
||||
);
|
||||
|
||||
// Verify semantic structure
|
||||
|
||||
@@ -63,25 +63,25 @@ describe("Blog Core Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Verify the section exists
|
||||
expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent(
|
||||
"Related Articles"
|
||||
"Related Articles",
|
||||
);
|
||||
|
||||
// Verify thumbnails are rendered
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid")
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy")
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy"),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Current post should not be displayed
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-resolving-active-conflicts")
|
||||
screen.queryByTestId("thumbnail-resolving-active-conflicts"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -90,20 +90,20 @@ describe("Blog Core Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Current post should not be displayed
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-resolving-active-conflicts")
|
||||
screen.queryByTestId("thumbnail-resolving-active-conflicts"),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// Other posts should be displayed
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid")
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy")
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -112,19 +112,19 @@ describe("Blog Core Integration", () => {
|
||||
|
||||
// All posts should be displayed
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-resolving-active-conflicts")
|
||||
screen.getByTestId("thumbnail-resolving-active-conflicts"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid")
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy")
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle empty related posts array", () => {
|
||||
const { container } = render(
|
||||
<RelatedArticles relatedPosts={[]} currentPostSlug="test-post" />
|
||||
<RelatedArticles relatedPosts={[]} currentPostSlug="test-post" />,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
@@ -135,7 +135,7 @@ describe("Blog Core Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Verify links are created correctly
|
||||
@@ -148,11 +148,11 @@ describe("Blog Core Integration", () => {
|
||||
|
||||
expect(operationalLink).toHaveAttribute(
|
||||
"href",
|
||||
"/blog/operational-security-mutual-aid"
|
||||
"/blog/operational-security-mutual-aid",
|
||||
);
|
||||
expect(hierarchyLink).toHaveAttribute(
|
||||
"href",
|
||||
"/blog/making-decisions-without-hierarchy"
|
||||
"/blog/making-decisions-without-hierarchy",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -161,11 +161,11 @@ describe("Blog Core Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent(
|
||||
"Related Articles"
|
||||
"Related Articles",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("Content Processing Integration", () => {
|
||||
const result = getBlogPostFiles();
|
||||
|
||||
expect(fs.readdirSync).toHaveBeenCalledWith(
|
||||
path.join(process.cwd(), "content/blog")
|
||||
path.join(process.cwd(), "content/blog"),
|
||||
);
|
||||
expect(result).toEqual(["post1.md", "post2.md", "post3.md"]);
|
||||
});
|
||||
@@ -89,7 +89,7 @@ Content with **bold** and *italic* text.`;
|
||||
const result = markdownToHtml(markdown);
|
||||
|
||||
expect(result).toContain(
|
||||
"<h1>Title with Special Characters: & < > \" '</h1>"
|
||||
"<h1>Title with Special Characters: & < > \" '</h1>",
|
||||
);
|
||||
expect(result).toContain("<strong>bold</strong>");
|
||||
expect(result).toContain("<em>italic</em>");
|
||||
|
||||
@@ -75,23 +75,23 @@ describe("Related Articles Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Current post should not be displayed
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-resolving-active-conflicts")
|
||||
screen.queryByTestId("thumbnail-resolving-active-conflicts"),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// Other posts should be displayed
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid")
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy")
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-building-community-trust")
|
||||
screen.getByTestId("thumbnail-building-community-trust"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -100,16 +100,16 @@ describe("Related Articles Integration", () => {
|
||||
|
||||
// All posts should be displayed
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-resolving-active-conflicts")
|
||||
screen.getByTestId("thumbnail-resolving-active-conflicts"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid")
|
||||
screen.getByTestId("thumbnail-operational-security-mutual-aid"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy")
|
||||
screen.getByTestId("thumbnail-making-decisions-without-hierarchy"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-building-community-trust")
|
||||
screen.getByTestId("thumbnail-building-community-trust"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -118,24 +118,24 @@ describe("Related Articles Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Verify links are created correctly
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-link-operational-security-mutual-aid")
|
||||
screen.getByTestId("thumbnail-link-operational-security-mutual-aid"),
|
||||
).toHaveAttribute("href", "/blog/operational-security-mutual-aid");
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-link-making-decisions-without-hierarchy")
|
||||
screen.getByTestId("thumbnail-link-making-decisions-without-hierarchy"),
|
||||
).toHaveAttribute("href", "/blog/making-decisions-without-hierarchy");
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-link-building-community-trust")
|
||||
screen.getByTestId("thumbnail-link-building-community-trust"),
|
||||
).toHaveAttribute("href", "/blog/building-community-trust");
|
||||
});
|
||||
|
||||
it("should handle empty related posts array", () => {
|
||||
const { container } = render(
|
||||
<RelatedArticles relatedPosts={[]} currentPostSlug="test-post" />
|
||||
<RelatedArticles relatedPosts={[]} currentPostSlug="test-post" />,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
@@ -148,14 +148,14 @@ describe("Related Articles Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={singlePost}
|
||||
currentPostSlug="different-post"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-resolving-active-conflicts")
|
||||
screen.getByTestId("thumbnail-resolving-active-conflicts"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-operational-security-mutual-aid")
|
||||
screen.queryByTestId("thumbnail-operational-security-mutual-aid"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ describe("Related Articles Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={currentPostOnly}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
@@ -177,11 +177,11 @@ describe("Related Articles Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="resolving-active-conflicts"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent(
|
||||
"Related Articles"
|
||||
"Related Articles",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -197,12 +197,12 @@ describe("Related Articles Integration", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug={slug}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Verify consistent structure
|
||||
expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent(
|
||||
"Related Articles"
|
||||
"Related Articles",
|
||||
);
|
||||
// Check that we have some thumbnails (the exact ones depend on the current post)
|
||||
const thumbnails = screen.getAllByTestId(/thumbnail-/);
|
||||
|
||||
@@ -131,7 +131,7 @@ describe("BlogPostPage", () => {
|
||||
"min-h-screen",
|
||||
"bg-[#F4F3F1]",
|
||||
"relative",
|
||||
"overflow-hidden"
|
||||
"overflow-hidden",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -144,7 +144,7 @@ describe("BlogPostPage", () => {
|
||||
expect(screen.getByTestId("content-banner")).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Article Title")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("This is a test article description")
|
||||
screen.getByText("This is a test article description"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ describe("BlogPostPage", () => {
|
||||
expect(article).toBeInTheDocument();
|
||||
expect(article).toHaveClass(
|
||||
"p-[var(--spacing-scale-024)]",
|
||||
"sm:py-[var(--spacing-scale-032)]"
|
||||
"sm:py-[var(--spacing-scale-032)]",
|
||||
);
|
||||
|
||||
// Check content is rendered
|
||||
@@ -188,7 +188,7 @@ describe("BlogPostPage", () => {
|
||||
expect(screen.getByTestId("ask-organizer")).toBeInTheDocument();
|
||||
expect(screen.getByText("Still have questions?")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Get answers from an experienced organizer")
|
||||
screen.getByText("Get answers from an experienced organizer"),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Ask an organizer")).toBeInTheDocument();
|
||||
});
|
||||
@@ -220,7 +220,7 @@ describe("BlogPostPage", () => {
|
||||
expect(contentDiv).toHaveClass("post-body");
|
||||
expect(contentDiv).toHaveClass("-mt-[var(--spacing-scale-048)]");
|
||||
expect(contentDiv).toHaveClass(
|
||||
"text-[var(--color-content-inverse-primary)]"
|
||||
"text-[var(--color-content-inverse-primary)]",
|
||||
);
|
||||
expect(contentDiv).toHaveClass("text-[16px]");
|
||||
expect(contentDiv).toHaveClass("leading-[24px]");
|
||||
@@ -267,7 +267,7 @@ describe("BlogPostPage", () => {
|
||||
|
||||
// Check for script elements using querySelector since RTL ignores them
|
||||
const scripts = document.querySelectorAll(
|
||||
'script[type="application/ld+json"]'
|
||||
'script[type="application/ld+json"]',
|
||||
);
|
||||
expect(scripts).toHaveLength(2);
|
||||
|
||||
@@ -285,7 +285,7 @@ describe("BlogPostPage", () => {
|
||||
// The component should throw an error when post is null
|
||||
// This happens because notFound() is called
|
||||
await expect(
|
||||
BlogPostPage({ params: { slug: "non-existent" } })
|
||||
BlogPostPage({ params: { slug: "non-existent" } }),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
@@ -297,7 +297,7 @@ describe("BlogPostPage", () => {
|
||||
|
||||
// Current post should not appear in related articles
|
||||
expect(
|
||||
screen.queryByTestId("related-test-article")
|
||||
screen.queryByTestId("related-test-article"),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// Other related posts should appear
|
||||
@@ -322,7 +322,7 @@ describe("BlogPostPage", () => {
|
||||
"top-1/4",
|
||||
"right-0",
|
||||
"pointer-events-none",
|
||||
"z-10"
|
||||
"z-10",
|
||||
);
|
||||
|
||||
// Second shape (left side)
|
||||
@@ -334,7 +334,7 @@ describe("BlogPostPage", () => {
|
||||
"top-1/2",
|
||||
"left-0",
|
||||
"pointer-events-none",
|
||||
"z-10"
|
||||
"z-10",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ describe("ContentBanner", () => {
|
||||
|
||||
// Check that the banner container exists - it's the first div with the specific classes
|
||||
const banner = document.querySelector(
|
||||
"div[class*='pt-[var(--measures-spacing-016)]']"
|
||||
"div[class*='pt-[var(--measures-spacing-016)]']",
|
||||
);
|
||||
expect(banner).toBeInTheDocument();
|
||||
expect(banner).toHaveClass(
|
||||
@@ -54,7 +54,7 @@ describe("ContentBanner", () => {
|
||||
"xl:h-[504px]",
|
||||
"relative",
|
||||
"w-full",
|
||||
"sm:overflow-hidden"
|
||||
"sm:overflow-hidden",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ describe("ContentBanner", () => {
|
||||
|
||||
// Check for background div with correct styling
|
||||
const backgroundDiv = document.querySelector(
|
||||
"div[style*='background-image']"
|
||||
"div[style*='background-image']",
|
||||
);
|
||||
expect(backgroundDiv).toBeInTheDocument();
|
||||
expect(backgroundDiv).toHaveClass(
|
||||
@@ -73,7 +73,7 @@ describe("ContentBanner", () => {
|
||||
"h-full",
|
||||
"bg-cover",
|
||||
"bg-no-repeat",
|
||||
"aspect-[320/225.5]"
|
||||
"aspect-[320/225.5]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -82,7 +82,7 @@ describe("ContentBanner", () => {
|
||||
|
||||
// Check for the md+ background div
|
||||
const mdBackgroundDiv = document.querySelector(
|
||||
"div[style*='Content_Banner_2.svg']"
|
||||
"div[style*='Content_Banner_2.svg']",
|
||||
);
|
||||
expect(mdBackgroundDiv).toBeInTheDocument();
|
||||
expect(mdBackgroundDiv).toHaveClass("hidden", "md:block");
|
||||
@@ -98,7 +98,7 @@ describe("ContentBanner", () => {
|
||||
render(<ContentBanner post={mockPost} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("This is a test article description")
|
||||
screen.getByText("This is a test article description"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ describe("ContentBanner", () => {
|
||||
|
||||
// Check the content container div
|
||||
const contentContainer = document.querySelector(
|
||||
"div[class*='relative z-10']"
|
||||
"div[class*='relative z-10']",
|
||||
);
|
||||
expect(contentContainer).toBeInTheDocument();
|
||||
expect(contentContainer).toHaveClass(
|
||||
@@ -122,7 +122,7 @@ describe("ContentBanner", () => {
|
||||
"z-10",
|
||||
"h-full",
|
||||
"flex",
|
||||
"flex-col"
|
||||
"flex-col",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -135,7 +135,7 @@ describe("ContentBanner", () => {
|
||||
"font-medium",
|
||||
"text-[18px]",
|
||||
"leading-[120%]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
|
||||
const description = screen.getByText("This is a test article description");
|
||||
@@ -144,7 +144,7 @@ describe("ContentBanner", () => {
|
||||
"font-normal",
|
||||
"text-[12px]",
|
||||
"leading-[16px]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -157,7 +157,7 @@ describe("ContentBanner", () => {
|
||||
"font-normal",
|
||||
"text-[10px]",
|
||||
"leading-[14px]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
|
||||
const date = screen.getByText("April 2025");
|
||||
@@ -166,7 +166,7 @@ describe("ContentBanner", () => {
|
||||
"font-normal",
|
||||
"text-[10px]",
|
||||
"leading-[14px]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -175,7 +175,7 @@ describe("ContentBanner", () => {
|
||||
|
||||
// Check the ContentContainer spacing
|
||||
const contentContainer = document.querySelector(
|
||||
"div[class*='relative z-20']"
|
||||
"div[class*='relative z-20']",
|
||||
);
|
||||
expect(contentContainer).toHaveClass("gap-[var(--measures-spacing-012)]");
|
||||
});
|
||||
@@ -184,13 +184,13 @@ describe("ContentBanner", () => {
|
||||
render(<ContentBanner post={mockPost} />);
|
||||
|
||||
const outerContainer = document.querySelector(
|
||||
"div[class*='pt-[var(--measures-spacing-016)]']"
|
||||
"div[class*='pt-[var(--measures-spacing-016)]']",
|
||||
);
|
||||
expect(outerContainer).toHaveClass(
|
||||
"pt-[var(--measures-spacing-016)]",
|
||||
"md:pt-[var(--measures-spacing-008)]",
|
||||
"lg:pt-[50px]",
|
||||
"xl:pt-[112px]"
|
||||
"xl:pt-[112px]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -216,7 +216,7 @@ describe("ContentBanner", () => {
|
||||
"sm:text-[24px]",
|
||||
"md:text-[32px]",
|
||||
"lg:text-[44px]",
|
||||
"xl:text-[64px]"
|
||||
"xl:text-[64px]",
|
||||
);
|
||||
|
||||
const description = screen.getByText("This is a test article description");
|
||||
@@ -224,7 +224,7 @@ describe("ContentBanner", () => {
|
||||
"sm:text-[14px]",
|
||||
"md:text-[14px]",
|
||||
"lg:text-[18px]",
|
||||
"xl:text-[24px]"
|
||||
"xl:text-[24px]",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ describe("ContentContainer", () => {
|
||||
"z-20",
|
||||
"h-full",
|
||||
"flex",
|
||||
"flex-col"
|
||||
"flex-col",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ describe("ContentContainer", () => {
|
||||
"font-medium",
|
||||
"text-[18px]",
|
||||
"leading-[120%]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ describe("ContentContainer", () => {
|
||||
"font-normal",
|
||||
"text-[12px]",
|
||||
"leading-[16px]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -108,7 +108,7 @@ describe("ContentContainer", () => {
|
||||
|
||||
// Check the content container (parent of icon)
|
||||
expect(iconContainer.parentElement).toHaveClass(
|
||||
"gap-[var(--measures-spacing-008)]"
|
||||
"gap-[var(--measures-spacing-008)]",
|
||||
);
|
||||
// Check the text container (parent of title) - it has responsive gap classes
|
||||
expect(textContainer.parentElement).toHaveClass("flex", "flex-col");
|
||||
@@ -121,7 +121,7 @@ describe("ContentContainer", () => {
|
||||
expect(metadataContainer).toHaveClass(
|
||||
"flex",
|
||||
"items-center",
|
||||
"gap-[var(--measures-spacing-008)]"
|
||||
"gap-[var(--measures-spacing-008)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -134,7 +134,7 @@ describe("ContentContainer", () => {
|
||||
"font-normal",
|
||||
"text-[10px]",
|
||||
"leading-[14px]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
|
||||
const date = screen.getByText("April 2025");
|
||||
@@ -143,7 +143,7 @@ describe("ContentContainer", () => {
|
||||
"font-normal",
|
||||
"text-[10px]",
|
||||
"leading-[14px]",
|
||||
"text-[var(--color-content-inverse-brand-royal)]"
|
||||
"text-[var(--color-content-inverse-brand-royal)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -229,7 +229,7 @@ describe("ContentContainer", () => {
|
||||
render(<ContentContainer post={longTitlePost} />);
|
||||
|
||||
expect(
|
||||
screen.getByText(/This is a very long article title/)
|
||||
screen.getByText(/This is a very long article title/),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -246,7 +246,7 @@ describe("ContentContainer", () => {
|
||||
render(<ContentContainer post={longDescPost} />);
|
||||
|
||||
expect(
|
||||
screen.getByText(/This is a very long article description/)
|
||||
screen.getByText(/This is a very long article description/),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ describe("ContentThumbnailTemplate", () => {
|
||||
|
||||
expect(screen.getByText("Test Blog Post Title")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/This is a test description/)
|
||||
screen.getByText(/This is a test description/),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -79,7 +79,7 @@ describe("ContentThumbnailTemplate", () => {
|
||||
|
||||
expect(screen.getByText("Test Blog Post Title")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/This is a test description/)
|
||||
screen.getByText(/This is a test description/),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Author")).toBeInTheDocument();
|
||||
});
|
||||
@@ -88,7 +88,7 @@ describe("ContentThumbnailTemplate", () => {
|
||||
describe("Props and Customization", () => {
|
||||
it("should apply custom className", () => {
|
||||
render(
|
||||
<ContentThumbnailTemplate post={mockPost} className="custom-class" />
|
||||
<ContentThumbnailTemplate post={mockPost} className="custom-class" />,
|
||||
);
|
||||
|
||||
const container = screen.getByRole("link");
|
||||
|
||||
+15
-15
@@ -27,7 +27,7 @@ describe("Footer", () => {
|
||||
expect(schemaData.email).toBe("medlab@colorado.edu");
|
||||
expect(schemaData.url).toBe("https://communityrule.com");
|
||||
expect(schemaData.sameAs).toContain(
|
||||
"https://bsky.app/profile/medlabboulder"
|
||||
"https://bsky.app/profile/medlabboulder",
|
||||
);
|
||||
expect(schemaData.sameAs).toContain("https://gitlab.com/medlabboulder");
|
||||
});
|
||||
@@ -36,7 +36,7 @@ describe("Footer", () => {
|
||||
render(<Footer />);
|
||||
|
||||
expect(
|
||||
screen.getAllByText("Media Economies Design Lab").length
|
||||
screen.getAllByText("Media Economies Design Lab").length,
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
const emailLinks = screen.getAllByRole("link", {
|
||||
@@ -86,13 +86,13 @@ describe("Footer", () => {
|
||||
render(<Footer />);
|
||||
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "Use cases" }).length
|
||||
screen.getAllByRole("link", { name: "Use cases" }).length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "Learn" }).length
|
||||
screen.getAllByRole("link", { name: "Learn" }).length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "About" }).length
|
||||
screen.getAllByRole("link", { name: "About" }).length,
|
||||
).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
@@ -100,13 +100,13 @@ describe("Footer", () => {
|
||||
render(<Footer />);
|
||||
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "Privacy Policy" }).length
|
||||
screen.getAllByRole("link", { name: "Privacy Policy" }).length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "Terms of Service" }).length
|
||||
screen.getAllByRole("link", { name: "Terms of Service" }).length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "Cookies Settings" }).length
|
||||
screen.getAllByRole("link", { name: "Cookies Settings" }).length,
|
||||
).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ describe("Footer", () => {
|
||||
render(<Footer />);
|
||||
|
||||
expect(screen.getAllByText("© All right reserved").length).toBeGreaterThan(
|
||||
0
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -123,7 +123,7 @@ describe("Footer", () => {
|
||||
|
||||
// Check that logo containers exist for different breakpoints
|
||||
const logoContainers = document.querySelectorAll(
|
||||
'[class*="block sm:hidden"], [class*="hidden sm:block lg:hidden"], [class*="hidden lg:block"]'
|
||||
'[class*="block sm:hidden"], [class*="hidden sm:block lg:hidden"], [class*="hidden lg:block"]',
|
||||
);
|
||||
expect(logoContainers.length).toBeGreaterThan(0);
|
||||
});
|
||||
@@ -147,7 +147,7 @@ describe("Footer", () => {
|
||||
|
||||
// The Separator component should be rendered (it uses a div with border, not hr)
|
||||
const separator = document.querySelector(
|
||||
".bg-\\[var\\(--border-color-default-secondary\\)\\]"
|
||||
".bg-\\[var\\(--border-color-default-secondary\\)\\]",
|
||||
);
|
||||
expect(separator).toBeInTheDocument();
|
||||
});
|
||||
@@ -263,10 +263,10 @@ describe("Footer", () => {
|
||||
expect(emailLink).toHaveClass("focus:ring-2");
|
||||
expect(emailLink).toHaveClass("focus:ring-offset-2");
|
||||
expect(emailLink).toHaveClass(
|
||||
"focus:ring-[var(--color-content-default-primary)]"
|
||||
"focus:ring-[var(--color-content-default-primary)]",
|
||||
);
|
||||
expect(emailLink).toHaveClass(
|
||||
"focus:ring-offset-[var(--color-surface-default-primary)]"
|
||||
"focus:ring-offset-[var(--color-surface-default-primary)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -276,10 +276,10 @@ describe("Footer", () => {
|
||||
expect(link).toHaveClass("focus:ring-2");
|
||||
expect(link).toHaveClass("focus:ring-offset-2");
|
||||
expect(link).toHaveClass(
|
||||
"focus:ring-[var(--color-content-default-primary)]"
|
||||
"focus:ring-[var(--color-content-default-primary)]",
|
||||
);
|
||||
expect(link).toHaveClass(
|
||||
"focus:ring-offset-[var(--color-surface-default-primary)]"
|
||||
"focus:ring-offset-[var(--color-surface-default-primary)]",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+10
-10
@@ -22,14 +22,14 @@ describe("Header", () => {
|
||||
|
||||
// Check main header structure - use container to scope the search
|
||||
const header = container.querySelector(
|
||||
'[role="banner"][aria-label="Main navigation header"]'
|
||||
'[role="banner"][aria-label="Main navigation header"]',
|
||||
);
|
||||
expect(header).toBeInTheDocument();
|
||||
expect(header).toHaveAttribute("aria-label", "Main navigation header");
|
||||
|
||||
// Check navigation - use container to scope the search
|
||||
const nav = container.querySelector(
|
||||
'[role="navigation"][aria-label="Main navigation"]'
|
||||
'[role="navigation"][aria-label="Main navigation"]',
|
||||
);
|
||||
expect(nav).toBeInTheDocument();
|
||||
expect(nav).toHaveAttribute("aria-label", "Main navigation");
|
||||
@@ -41,15 +41,15 @@ describe("Header", () => {
|
||||
// Check all navigation items have proper aria-labels - use menuitem role since they're in a menubar
|
||||
expect(
|
||||
screen.getAllByRole("menuitem", { name: "Navigate to Use cases page" })
|
||||
.length
|
||||
.length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.getAllByRole("menuitem", { name: "Navigate to Learn page" })
|
||||
.length
|
||||
.length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.getAllByRole("menuitem", { name: "Navigate to About page" })
|
||||
.length
|
||||
.length,
|
||||
).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
@@ -59,7 +59,7 @@ describe("Header", () => {
|
||||
render(<Header onToggle={mockOnToggle} />);
|
||||
|
||||
const script = document.querySelector(
|
||||
'script[type="application/ld+json"]'
|
||||
'script[type="application/ld+json"]',
|
||||
);
|
||||
expect(script).toBeInTheDocument();
|
||||
|
||||
@@ -296,7 +296,7 @@ describe("Header", () => {
|
||||
(img) =>
|
||||
img.alt === "Avatar 1" ||
|
||||
img.alt === "Avatar 2" ||
|
||||
img.alt === "Avatar 3"
|
||||
img.alt === "Avatar 3",
|
||||
);
|
||||
expect(avatarImages.length).toBeGreaterThan(0);
|
||||
});
|
||||
@@ -324,17 +324,17 @@ describe("Header", () => {
|
||||
const { container } = render(<Header onToggle={mockOnToggle} />);
|
||||
|
||||
const header = container.querySelector(
|
||||
'[role="banner"][aria-label="Main navigation header"]'
|
||||
'[role="banner"][aria-label="Main navigation header"]',
|
||||
);
|
||||
expect(header).toHaveClass("bg-[var(--color-surface-default-primary)]");
|
||||
expect(header).toHaveClass("w-full");
|
||||
expect(header).toHaveClass("border-b");
|
||||
expect(header).toHaveClass(
|
||||
"border-[var(--border-color-default-tertiary)]"
|
||||
"border-[var(--border-color-default-tertiary)]",
|
||||
);
|
||||
|
||||
const nav = container.querySelector(
|
||||
'[role="navigation"][aria-label="Main navigation"]'
|
||||
'[role="navigation"][aria-label="Main navigation"]',
|
||||
);
|
||||
expect(nav).toHaveClass("flex");
|
||||
expect(nav).toHaveClass("items-center");
|
||||
|
||||
@@ -52,13 +52,13 @@ describe("Logo Component", () => {
|
||||
const { rerender } = render(<Logo size="homeHeaderMd" />);
|
||||
let textElement = screen.getByText("CommunityRule");
|
||||
expect(textElement).toHaveClass(
|
||||
"text-[var(--color-content-inverse-primary)]"
|
||||
"text-[var(--color-content-inverse-primary)]",
|
||||
);
|
||||
|
||||
rerender(<Logo size="header" />);
|
||||
textElement = screen.getByText("CommunityRule");
|
||||
expect(textElement).toHaveClass(
|
||||
"text-[var(--color-content-default-primary)]"
|
||||
"text-[var(--color-content-default-primary)]",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -73,14 +73,14 @@ describe("RelatedArticles", () => {
|
||||
<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)]"
|
||||
"lg:py-[var(--spacing-scale-064)]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -89,7 +89,7 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
const heading = screen.getByRole("heading", { level: 2 });
|
||||
@@ -101,7 +101,7 @@ describe("RelatedArticles", () => {
|
||||
"leading-[110%]",
|
||||
"font-medium",
|
||||
"text-[var(--color-content-inverse-primary)]",
|
||||
"text-center"
|
||||
"text-center",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -110,17 +110,17 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-1")
|
||||
screen.getByTestId("thumbnail-related-article-1"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-2")
|
||||
screen.getByTestId("thumbnail-related-article-2"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-3")
|
||||
screen.getByTestId("thumbnail-related-article-3"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -142,29 +142,29 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={postsWithCurrent}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
// Should not render the current article
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-current-article")
|
||||
screen.queryByTestId("thumbnail-current-article"),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// Should still render the other related articles
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-1")
|
||||
screen.getByTestId("thumbnail-related-article-1"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-2")
|
||||
screen.getByTestId("thumbnail-related-article-2"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-3")
|
||||
screen.getByTestId("thumbnail-related-article-3"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders nothing when no related posts", () => {
|
||||
const { container } = render(
|
||||
<RelatedArticles relatedPosts={[]} currentPostSlug="current-article" />
|
||||
<RelatedArticles relatedPosts={[]} currentPostSlug="current-article" />,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
@@ -187,7 +187,7 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={currentPostOnly}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeNull();
|
||||
@@ -198,7 +198,7 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
const container = document.querySelector("section > div");
|
||||
@@ -206,7 +206,7 @@ describe("RelatedArticles", () => {
|
||||
"flex",
|
||||
"flex-col",
|
||||
"gap-[var(--spacing-scale-032)]",
|
||||
"lg:gap-[51px]"
|
||||
"lg:gap-[51px]",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -215,14 +215,14 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
const articlesContainer = document.querySelector("section > div > div");
|
||||
expect(articlesContainer).toHaveClass(
|
||||
"flex",
|
||||
"justify-center",
|
||||
"overflow-hidden"
|
||||
"overflow-hidden",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -238,17 +238,17 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
const carouselContainer = document.querySelector(
|
||||
"section > div > div > div"
|
||||
"section > div > div > div",
|
||||
);
|
||||
expect(carouselContainer).toHaveClass(
|
||||
"overflow-x-auto",
|
||||
"scrollbar-hide",
|
||||
"cursor-grab",
|
||||
"active:cursor-grabbing"
|
||||
"active:cursor-grabbing",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -264,16 +264,16 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
const carouselContainer = document.querySelector(
|
||||
"section > div > div > div"
|
||||
"section > div > div > div",
|
||||
);
|
||||
expect(carouselContainer).toHaveClass(
|
||||
"transition-transform",
|
||||
"duration-500",
|
||||
"ease-in-out"
|
||||
"ease-in-out",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -284,17 +284,17 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={singlePost}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-1")
|
||||
screen.getByTestId("thumbnail-related-article-1"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-related-article-2")
|
||||
screen.queryByTestId("thumbnail-related-article-2"),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-related-article-3")
|
||||
screen.queryByTestId("thumbnail-related-article-3"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -305,17 +305,17 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={twoPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-1")
|
||||
screen.getByTestId("thumbnail-related-article-1"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-2")
|
||||
screen.getByTestId("thumbnail-related-article-2"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId("thumbnail-related-article-3")
|
||||
screen.queryByTestId("thumbnail-related-article-3"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -324,7 +324,7 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
const section = document.querySelector("section");
|
||||
@@ -336,11 +336,11 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={mockRelatedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
const carouselContainer = document.querySelector(
|
||||
"section > div > div > div"
|
||||
"section > div > div > div",
|
||||
);
|
||||
expect(carouselContainer).toHaveClass("gap-0");
|
||||
});
|
||||
@@ -350,13 +350,13 @@ describe("RelatedArticles", () => {
|
||||
|
||||
// Should still render all articles
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-1")
|
||||
screen.getByTestId("thumbnail-related-article-1"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-2")
|
||||
screen.getByTestId("thumbnail-related-article-2"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId("thumbnail-related-article-3")
|
||||
screen.getByTestId("thumbnail-related-article-3"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -386,7 +386,7 @@ describe("RelatedArticles", () => {
|
||||
<RelatedArticles
|
||||
relatedPosts={malformedPosts}
|
||||
currentPostSlug="current-article"
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("thumbnail-malformed-1")).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user