Match Home, top nav, hero, and logo wall to the current marketing frames.

Puts Workshops in the header, pairs the hero with a static rule card and breakpoint CTAs, and shows the three pilot marks with no wall label.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilalloandCursor committed 2026-11-02 18:50:32 -07:00
1 parent e12e06f7ce
commit 33cdaf135c
47 files changed
+728 -527

No files matched your search

+3 -3
View File
@@ -40,9 +40,9 @@ componentTestSuite<AskOrganizerProps>(config);
describe("AskOrganizer (behavioral tests)", () => {
it("renders title", () => {
render(<AskOrganizer title="Test Title" />);
expect(
screen.getByRole("heading", { name: "Test Title" }),
).toBeInTheDocument();
const title = screen.getByRole("heading", { name: "Test Title" });
expect(title).toBeInTheDocument();
expect(title.tagName).toBe("H2");
});
it("renders subtitle when provided", () => {
+5 -5
View File
@@ -38,9 +38,9 @@ componentTestSuite<FeatureGridProps>(config);
describe("FeatureGrid (behavioral tests)", () => {
it("renders title and subtitle", () => {
render(<FeatureGrid title="Test Title" subtitle="Test Subtitle" />);
expect(
screen.getByRole("heading", { name: "Test Title" }),
).toBeInTheDocument();
const title = screen.getByRole("heading", { name: "Test Title" });
expect(title).toBeInTheDocument();
expect(title.tagName).toBe("H2");
expect(screen.getByText("Test Subtitle")).toBeInTheDocument();
expect(
screen.queryByRole("heading", { name: "Test Subtitle" }),
@@ -58,10 +58,10 @@ describe("FeatureGrid (behavioral tests)", () => {
).not.toBeInTheDocument();
});
it("sends Explore the toolkit to the Learn page", () => {
it("sends Explore the patterns to the Learn page", () => {
render(<FeatureGrid title="Test" subtitle="Test" />);
expect(
screen.getByRole("link", { name: "Explore the toolkit" }),
screen.getByRole("link", { name: "Explore the patterns" }),
).toHaveAttribute("href", "/learn");
});
+16 -7
View File
@@ -20,8 +20,6 @@ const config: ComponentTestSuiteConfig<HeroBannerProps> = {
optionalProps: {
subtitle: "Subtitle",
description: "Description",
ctaText: "CTA",
ctaHref: "/link",
},
primaryRole: "region",
testCases: {
@@ -50,18 +48,29 @@ describe("HeroBanner (behavioral tests)", () => {
).toBeInTheDocument();
});
it("renders hero image", () => {
it("renders the team Rule card instead of a hero photograph", () => {
render(<HeroBanner title="Test" />);
expect(
screen.getByRole("img", { name: "Hero illustration" }),
screen.getByRole("heading", { name: "CommunityRule Creative Team" }),
).toBeInTheDocument();
expect(
screen.queryByRole("img", { name: "Hero illustration" }),
).not.toBeInTheDocument();
});
it("renders CTA with the inverse (black) filled palette", () => {
render(<HeroBanner title="Test" ctaText="Get Started" ctaHref="/start" />);
const cta = screen.getAllByRole("link", { name: "Get Started" })[0];
it("sends Talk to a facilitator to the workshops page", () => {
render(<HeroBanner title="Test" />);
const cta = screen.getAllByRole("link", { name: "Talk to a facilitator" })[0];
expect(cta).toHaveAttribute("href", "/workshops");
expect(cta?.className).toContain(
"bg-[var(--color-surface-default-primary)]",
);
});
it("renders Create rule as a button for large screens", () => {
render(<HeroBanner title="Test" />);
expect(
screen.getAllByRole("button", { name: "Create rule" }).length,
).toBeGreaterThan(0);
});
});
+10 -2
View File
@@ -79,10 +79,18 @@ describe("Logo (behavioral tests)", () => {
expect(row?.className).toContain("h-[var(--space-1000)]");
});
it("shows the folder-top wordmark without a breakpoint hide", () => {
it("hides the folder-top wordmark below md", () => {
render(<Logo size="topNavFolderTop" wordmark />);
const wordmark = screen.getByText("CommunityRule");
expect(wordmark.className).not.toMatch(/\bhidden\b/);
expect(wordmark.className).toMatch(/\bhidden\b/);
expect(wordmark.className).toContain("md:block");
});
it("hides the standard header wordmark below sm", () => {
render(<Logo size="topNavHeader" wordmark />);
const wordmark = screen.getByText("CommunityRule");
expect(wordmark.className).toContain("hidden");
expect(wordmark.className).toContain("sm:block");
});
it("renders with different size variants", () => {
+18 -10
View File
@@ -131,7 +131,7 @@ describe("Top header chrome", () => {
);
expect(
screen.getAllByRole("menuitem", {
name: /navigate to use cases page/i,
name: /navigate to workshops page/i,
hidden: true,
}),
).toHaveLength(1);
@@ -146,7 +146,7 @@ describe("Top header chrome", () => {
name: /go to your profile/i,
hidden: true,
}),
).toHaveLength(1);
).toHaveLength(2);
expect(
screen.getAllByRole("button", {
name: /create a new rule/i,
@@ -157,11 +157,18 @@ describe("Top header chrome", () => {
}
});
it("points Workshops at /workshops", () => {
renderWithProviders(<Top folderTop={false} />);
expect(
screen.getByRole("menuitem", { name: /navigate to workshops page/i }),
).toHaveAttribute("href", "/workshops");
});
it("shows Profile instead of Log in when signed in", () => {
renderWithProviders(<Top folderTop={false} loggedIn />);
expect(
screen.getByRole("menuitem", { name: /go to your profile/i }),
).toBeInTheDocument();
screen.getAllByRole("menuitem", { name: /go to your profile/i }).length,
).toBeGreaterThan(0);
expect(
screen.queryByRole("button", { name: /log in to your account/i }),
).not.toBeInTheDocument();
@@ -174,6 +181,7 @@ describe("Top header chrome", () => {
const headerNav = document.querySelector("header nav");
expect(logo?.className).not.toMatch(/\bz-20\b/);
expect(nav?.className).not.toMatch(/\babsolute\b/);
expect(headerNav?.className).toContain("flex");
expect(headerNav?.className).toContain(
"md:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]",
);
@@ -183,9 +191,8 @@ describe("Top header chrome", () => {
renderWithProviders(<Top folderTop />);
const tab = document.querySelector(".HeaderTab");
const nav = document.querySelector("[data-top='nav']");
expect(tab?.className).toContain("w-fit");
expect(tab?.className).toContain("sm:w-auto");
expect(tab?.className).toContain("sm:flex-1");
expect(tab?.className).toContain("flex-1");
expect(tab?.className).toContain("justify-between");
expect(nav?.className).toContain("md:absolute");
expect(nav?.className).toContain(
"md:left-[calc(50vw-var(--space-400))]",
@@ -193,16 +200,17 @@ describe("Top header chrome", () => {
expect(nav?.className).not.toContain("ml-auto");
});
it("keeps a single home nav cluster and shows the folder wordmark", () => {
it("keeps a single home nav cluster and hides the folder wordmark below md", () => {
renderWithProviders(<Top folderTop />);
expect(
screen.getAllByRole("menuitem", {
name: /navigate to use cases page/i,
name: /navigate to workshops page/i,
hidden: true,
}),
).toHaveLength(1);
const wordmark = screen.getByText("CommunityRule");
expect(wordmark.className).not.toMatch(/\bhidden\b/);
expect(wordmark.className).toMatch(/\bhidden\b/);
expect(wordmark.className).toContain("md:block");
expect(
screen.getByRole("button", { name: /create a new rule/i }).className,
).toContain("md:text-x-small-label");
+2 -2
View File
@@ -25,8 +25,8 @@ describe("TopWithPathname", () => {
it("renders Profile from the server session before /api/auth/session resolves", () => {
renderWithProviders(<TopWithPathname initialSignedIn />);
expect(
screen.getByRole("menuitem", { name: /go to your profile/i }),
).toBeInTheDocument();
screen.getAllByRole("menuitem", { name: /go to your profile/i }).length,
).toBeGreaterThan(0);
expect(
screen.queryByRole("button", { name: /log in to your account/i }),
).not.toBeInTheDocument();
@@ -83,4 +83,27 @@ describe("ContentLockup", () => {
expect(link.className).not.toMatch(/focus:ring-2/);
expect(link.className).toMatch(/focus-visible:outline/);
});
it("renders the title as h2 when headingLevel is h2", () => {
renderWithProviders(
<ContentLockup
variant="feature"
headingLevel="h2"
title="Tools for the parts that get hard"
/>,
);
expect(
screen.getByRole("heading", {
level: 2,
name: "Tools for the parts that get hard",
}),
).toBeInTheDocument();
expect(
screen.queryByRole("heading", {
level: 1,
name: "Tools for the parts that get hard",
}),
).not.toBeInTheDocument();
});
});
+22 -20
View File
@@ -7,40 +7,40 @@ test.describe("Critical User Journeys", () => {
test("complete user journey: learn about CommunityRule", async ({ page }) => {
// 1. User lands on homepage
await expect(page.locator("text=Collaborate")).toBeVisible();
await expect(page.locator("text=Build structure")).toBeVisible();
// 2. User reads hero section
await expect(
page.locator("text=Help your community make important decisions"),
page.locator("text=Strong communities agree on four things early"),
).toBeVisible();
// 3. User clicks CTA to learn more
const learnCta = page
.getByRole("link", { name: /Learn how CommunityRule works/i })
.or(page.getByRole("button", { name: /Learn how CommunityRule works/i }))
.getByRole("link", { name: /Talk to a facilitator/i })
.or(page.getByRole("button", { name: /Talk to a facilitator/i }))
.first();
if ((await learnCta.count()) > 0 && (await learnCta.isVisible())) {
await learnCta.click();
await expect(page).toHaveURL(/\/how-it-works/);
await expect(page).toHaveURL(/\/workshops/);
}
// 4. User scrolls to CardSteps section (home)
// Note: mobile title is one line; lg+ uses stacked "How / CommunityRule / helps" from home copy
const howItWorksHeading = page.locator(
'h2:has-text("How CommunityRule works"), h2:has-text("How"), h2:has-text("CommunityRule"), h2:has-text("helps")',
'h2:has-text("How CommunityRule helps"), h2:has-text("How"), h2:has-text("CommunityRule"), h2:has-text("helps")',
);
await expect(howItWorksHeading).toBeVisible();
// 5. User reads the process steps
await expect(
page.locator("text=Document how your community makes decisions"),
page.locator("text=See what has worked for other communities"),
).toBeVisible();
await expect(
page.locator("text=Build an operating manual for a successful community"),
page.locator("text=Decide how you will decide, while things are calm"),
).toBeVisible();
await expect(
page.locator(
"text=Get a link to your manual for your group to review and evolve",
"text=Invite your members and change it together as you grow",
),
).toBeVisible();
@@ -87,20 +87,22 @@ test.describe("Critical User Journeys", () => {
// Check main sections are present
await expect(
page.locator("h1, h2").filter({ hasText: "Collaborate" }),
page.locator("h1, h2").filter({ hasText: "Build structure" }),
).toBeVisible();
const howItWorksHeading = page.locator(
'h2:has-text("How CommunityRule works"), h2:has-text("How CommunityRule helps")',
'h2:has-text("How CommunityRule helps"), h2:has-text("How CommunityRule helps")',
);
await expect(howItWorksHeading).toBeVisible();
await expect(
page.locator("h1").filter({ hasText: "Tools for the parts that get hard" }),
page.locator("h2").filter({ hasText: "Tools for the parts that get hard" }),
).toBeVisible();
// Check key components are rendered
await expect(page.locator('img[alt="Hero illustration"]')).toBeVisible();
await expect(
page.getByRole("heading", { name: "CommunityRule Creative Team" }),
).toBeVisible();
await expect(page.locator('img[alt="Food Not Bombs"]')).toBeVisible();
await expect(page.locator("text=Jo Freeman")).toBeVisible();
});
@@ -108,11 +110,11 @@ test.describe("Critical User Journeys", () => {
test("feature grid section functionality", async ({ page }) => {
// Check section header
await expect(
page.locator('h1:has-text("Tools for the parts that get hard")'),
page.locator('h2:has-text("Tools for the parts that get hard")'),
).toBeVisible();
await expect(
page.locator(
"text=Every group runs into the same knots. CommunityRule gives you a way through",
"text=Every community runs into the same knots. CommunityRule gives you a way through",
),
).toBeVisible();
@@ -127,12 +129,12 @@ test.describe("Critical User Journeys", () => {
featureSection.locator("text=Plan for conflict"),
).toBeVisible();
// Feature tiles are presentational — only the lockup "Explore the toolkit" is a link
// Feature tiles are presentational — only the lockup "Explore the patterns" is a link
await expect(
featureSection.locator('a[href="#decision-making"]'),
).toHaveCount(0);
await expect(
featureSection.getByRole("link", { name: "Explore the toolkit" }),
featureSection.getByRole("link", { name: "Explore the patterns" }),
).toHaveAttribute("href", "/learn");
});
@@ -186,19 +188,19 @@ test.describe("Critical User Journeys", () => {
// Test mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await expect(
page.locator("h1, h2").filter({ hasText: "Collaborate" }),
page.locator("h1, h2").filter({ hasText: "Build structure" }),
).toBeVisible();
// Test tablet viewport
await page.setViewportSize({ width: 768, height: 1024 });
await expect(
page.locator("h1, h2").filter({ hasText: "Collaborate" }),
page.locator("h1, h2").filter({ hasText: "Build structure" }),
).toBeVisible();
// Test desktop viewport
await page.setViewportSize({ width: 1440, height: 900 });
await expect(
page.locator("h1, h2").filter({ hasText: "Collaborate" }),
page.locator("h1, h2").filter({ hasText: "Build structure" }),
).toBeVisible();
});
});
+17 -48
View File
@@ -18,7 +18,7 @@ test.describe("Edge Cases and Error Scenarios", () => {
await page.goto("/", { waitUntil: "domcontentloaded", timeout: 10000 });
// Page should still load eventually
await expect(page.locator("text=Collaborate")).toBeVisible({
await expect(page.locator("text=Build structure")).toBeVisible({
timeout: 10000,
});
});
@@ -38,7 +38,7 @@ test.describe("Edge Cases and Error Scenarios", () => {
await expect(body).toBeVisible();
// Verify key content is still accessible
await expect(page.locator("text=Collaborate")).toBeVisible();
await expect(page.locator("text=Build structure")).toBeVisible();
});
test("handles JavaScript errors gracefully", async ({ page }) => {
@@ -59,30 +59,19 @@ test.describe("Edge Cases and Error Scenarios", () => {
});
// Page should continue to function
await expect(page.locator("text=Collaborate")).toBeVisible();
await expect(page.locator("text=Build structure")).toBeVisible();
const learnCta = page
.getByRole("link", { name: /Learn how CommunityRule works/i })
.or(page.getByRole("button", { name: /Learn how CommunityRule works/i }));
const ctaCount = await learnCta.count();
let visibleCta = null;
for (let i = 0; i < ctaCount; i++) {
const cta = learnCta.nth(i);
if (await cta.isVisible()) {
visibleCta = cta;
break;
}
const createCta = page.getByRole("button", { name: "Create rule" });
const facilitatorCta = page.getByRole("link", {
name: /Talk to a facilitator/i,
});
const visibleCreate = createCta.filter({ has: page.locator(":visible") });
if ((await visibleCreate.count()) > 0) {
await visibleCreate.first().click();
await expect(page).toHaveURL(/\/create/);
} else {
await expect(facilitatorCta.first()).toBeVisible();
}
if (!visibleCta) {
throw new Error(
'No visible "Learn how CommunityRule works" CTA found',
);
}
await visibleCta.click();
await expect(page).toHaveURL(/\/how-it-works/);
});
test("handles missing images gracefully", async ({ page }) => {
@@ -96,29 +85,9 @@ test.describe("Edge Cases and Error Scenarios", () => {
await page.goto("/", { waitUntil: "domcontentloaded" });
// Page should still function without images
await expect(page.locator("text=Collaborate")).toBeVisible();
const learnCta = page
.getByRole("link", { name: /Learn how CommunityRule works/i })
.or(page.getByRole("button", { name: /Learn how CommunityRule works/i }));
const ctaCount = await learnCta.count();
let visibleCta = null;
for (let i = 0; i < ctaCount; i++) {
const cta = learnCta.nth(i);
if (await cta.isVisible()) {
visibleCta = cta;
break;
}
}
if (!visibleCta) {
throw new Error(
'No visible "Learn how CommunityRule works" CTA found',
);
}
await visibleCta.click();
await expect(page).toHaveURL(/\/how-it-works/);
await expect(page.locator("text=Build structure")).toBeVisible();
await expect(
page.getByRole("heading", { name: "CommunityRule Creative Team" }),
).toBeVisible();
});
});
+30 -30
View File
@@ -11,11 +11,11 @@ describe("Page", () => {
render(<Page />);
// Check that all main sections are rendered (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
expect(screen.getAllByText("with clarity").length).toBeGreaterThan(0);
expect(screen.getAllByText("Build structure").length).toBeGreaterThan(0);
expect(screen.getAllByText("before you need it").length).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Help your community make important decisions in a way that reflects its unique values.",
"Strong communities agree on four things early. They settle how they talk, who belongs, how they decide and what happens when they disagree. Write yours down together.",
).length,
).toBeGreaterThan(0);
@@ -23,7 +23,7 @@ describe("Page", () => {
// Check CardSteps section (using getAllByText since there are multiple instances)
await waitFor(() => {
expect(
screen.getAllByText("How CommunityRule works").length,
screen.getAllByText("How CommunityRule helps").length,
).toBeGreaterThan(0);
});
expect(
@@ -41,7 +41,7 @@ describe("Page", () => {
});
expect(
screen.getAllByText(
"Every group runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
"Every community runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
).length,
).toBeGreaterThan(0);
@@ -58,20 +58,20 @@ describe("Page", () => {
render(<Page />);
// Check hero banner content (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
expect(screen.getAllByText("with clarity").length).toBeGreaterThan(0);
expect(screen.getAllByText("Build structure").length).toBeGreaterThan(0);
expect(screen.getAllByText("before you need it").length).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Help your community make important decisions in a way that reflects its unique values.",
"Strong communities agree on four things early. They settle how they talk, who belongs, how they decide and what happens when they disagree. Write yours down together.",
).length,
).toBeGreaterThan(0);
expect(
screen.getAllByText("Learn how CommunityRule works").length,
screen.getAllByText("Talk to a facilitator").length,
).toBeGreaterThan(0);
const learnLinks = screen.getAllByRole("link", {
name: "Learn how CommunityRule works",
name: "Talk to a facilitator",
});
expect(learnLinks[0]).toHaveAttribute("href", "/how-it-works");
expect(learnLinks[0]).toHaveAttribute("href", "/workshops");
});
test("renders CardSteps section with correct data", async () => {
@@ -80,7 +80,7 @@ describe("Page", () => {
// Wait for dynamically imported CardSteps component to load
await waitFor(() => {
expect(
screen.getAllByText("How CommunityRule works").length,
screen.getAllByText("How CommunityRule helps").length,
).toBeGreaterThan(0);
});
expect(
@@ -91,16 +91,16 @@ describe("Page", () => {
// Check individual card content (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("Document how your community makes decisions").length,
screen.getAllByText("See what has worked for other communities").length,
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Build an operating manual for a successful community",
"Decide how you will decide, while things are calm",
).length,
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Get a link to your manual for your group to review and evolve",
"Invite your members and change it together as you grow",
).length,
).toBeGreaterThan(0);
});
@@ -117,11 +117,11 @@ describe("Page", () => {
});
expect(
screen.getAllByText(
"Every group runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
"Every community runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
).length,
).toBeGreaterThan(0);
const learnMore = screen.getAllByRole("link", {
name: "Explore the toolkit",
name: "Explore the patterns",
});
expect(learnMore[0]).toHaveAttribute("href", "/learn");
});
@@ -144,14 +144,14 @@ describe("Page", () => {
// Check that all major components are present by looking for their content
// HeroBanner
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
expect(screen.getAllByText("Build structure").length).toBeGreaterThan(0);
// Wait for dynamically imported components to load
// LogoWall - should be present (even if just the component structure)
// CardSteps
await waitFor(() => {
expect(
screen.getAllByText("How CommunityRule works").length,
screen.getAllByText("How CommunityRule helps").length,
).toBeGreaterThan(0);
});
@@ -174,7 +174,7 @@ describe("Page", () => {
test("has correct page structure", () => {
render(<Page />);
const mainContainer = screen.getAllByText("Collaborate")[0].closest("div");
const mainContainer = screen.getAllByText("Build structure")[0].closest("div");
expect(mainContainer).toBeInTheDocument();
});
@@ -183,7 +183,7 @@ describe("Page", () => {
// Check CTA button in hero banner
expect(
screen.getAllByText("Learn how CommunityRule works").length,
screen.getAllByText("Talk to a facilitator").length,
).toBeGreaterThan(0);
// Check CTA button in ask organizer section
@@ -196,7 +196,7 @@ describe("Page", () => {
// Check main description (using getAllByText since there are multiple instances)
expect(
screen.getAllByText(
"Help your community make important decisions in a way that reflects its unique values.",
"Strong communities agree on four things early. They settle how they talk, who belongs, how they decide and what happens when they disagree. Write yours down together.",
).length,
).toBeGreaterThan(0);
@@ -213,7 +213,7 @@ describe("Page", () => {
await waitFor(() => {
expect(
screen.getAllByText(
"Every group runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
"Every community runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
).length,
).toBeGreaterThan(0);
});
@@ -228,12 +228,12 @@ describe("Page", () => {
render(<Page />);
// Check all section titles (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
expect(screen.getAllByText("Build structure").length).toBeGreaterThan(0);
// Wait for dynamically imported components
await waitFor(() => {
expect(
screen.getAllByText("How CommunityRule works").length,
screen.getAllByText("How CommunityRule helps").length,
).toBeGreaterThan(0);
});
await waitFor(() => {
@@ -254,18 +254,18 @@ describe("Page", () => {
await waitFor(() => {
// Three Step tiles inside CardSteps
expect(
screen.getAllByText("Document how your community makes decisions")
screen.getAllByText("See what has worked for other communities")
.length,
).toBeGreaterThan(0);
});
expect(
screen.getAllByText(
"Build an operating manual for a successful community",
"Decide how you will decide, while things are calm",
).length,
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Get a link to your manual for your group to review and evolve",
"Invite your members and change it together as you grow",
).length,
).toBeGreaterThan(0);
});
@@ -274,7 +274,7 @@ describe("Page", () => {
render(<Page />);
// Check subtitles (using getAllByText since there are multiple instances)
expect(screen.getAllByText("with clarity").length).toBeGreaterThan(0);
expect(screen.getAllByText("before you need it").length).toBeGreaterThan(0);
// Wait for dynamically imported components
await waitFor(() => {
@@ -287,7 +287,7 @@ describe("Page", () => {
await waitFor(() => {
expect(
screen.getAllByText(
"Every group runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
"Every community runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
).length,
).toBeGreaterThan(0);
});
+28 -28
View File
@@ -28,33 +28,31 @@ describe("Page Flow Integration", () => {
// Hero Banner section
expect(
screen.getByRole("heading", { name: "Collaborate" }),
screen.getByRole("heading", { name: "Build structure" }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { name: "with clarity" }),
screen.getByRole("heading", { name: "before you need it" }),
).toBeInTheDocument();
expect(
screen.getByText(
"Help your community make important decisions in a way that reflects its unique values.",
"Strong communities agree on four things early. They settle how they talk, who belongs, how they decide and what happens when they disagree. Write yours down together.",
),
).toBeInTheDocument();
const ctaButtons = screen.getAllByRole("link", {
name: "Learn how CommunityRule works",
name: "Talk to a facilitator",
});
expect(ctaButtons.length).toBeGreaterThan(0);
await waitFor(() => {
expect(screen.getByAltText("Food Not Bombs")).toBeInTheDocument();
});
expect(screen.getByAltText("Start COOP")).toBeInTheDocument();
expect(screen.getByAltText("Metagov")).toBeInTheDocument();
expect(screen.getByAltText("Open Civics")).toBeInTheDocument();
expect(screen.getByAltText("Mutual Aid CO")).toBeInTheDocument();
expect(screen.getByAltText("CU Boulder")).toBeInTheDocument();
expect(screen.getByAltText("Boulder Street Medics")).toBeInTheDocument();
expect(screen.getByAltText("Mutual Aid Colorado")).toBeInTheDocument();
expect(screen.queryByAltText("Start COOP")).not.toBeInTheDocument();
await waitFor(() => {
expect(
screen.getByRole("heading", { name: /How CommunityRule works/ }),
screen.getByRole("heading", { name: /How CommunityRule helps/ }),
).toBeInTheDocument();
});
expect(
@@ -63,25 +61,27 @@ describe("Page Flow Integration", () => {
),
).toBeInTheDocument();
expect(
screen.getByText("Document how your community makes decisions"),
screen.getByText("See what has worked for other communities"),
).toBeInTheDocument();
expect(
screen.getByText("Build an operating manual for a successful community"),
screen.getByText("Decide how you will decide, while things are calm"),
).toBeInTheDocument();
expect(
screen.getByText(
"Get a link to your manual for your group to review and evolve",
"Invite your members and change it together as you grow",
),
).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByRole("heading", { name: "Consensus" })).toBeInTheDocument();
expect(
screen.getByRole("heading", { name: "Devolution" }),
).toBeInTheDocument();
});
expect(
screen.getByRole("heading", { name: "Do-ocracy" }),
screen.getByRole("heading", { name: "Consensus" }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { name: "Devolution" }),
screen.getByRole("heading", { name: "Do-ocracy" }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { name: "Quadratic Governance" }),
@@ -96,7 +96,7 @@ describe("Page Flow Integration", () => {
});
expect(
screen.getByText(
"Every group runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
"Every community runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
),
).toBeInTheDocument();
@@ -120,11 +120,11 @@ describe("Page Flow Integration", () => {
renderPage();
const ctaLinks = screen.getAllByRole("link", {
name: "Learn how CommunityRule works",
name: "Talk to a facilitator",
});
const ctaLink = ctaLinks[0];
expect(ctaLink).toBeInTheDocument();
expect(ctaLink).toHaveAttribute("href", "/how-it-works");
expect(ctaLink).toHaveAttribute("href", "/workshops");
await user.click(ctaLink);
expect(ctaLink).toBeInTheDocument();
@@ -135,13 +135,13 @@ describe("Page Flow Integration", () => {
await waitFor(() => {
const cards = screen.getAllByText(
/Document how your community|Build an operating manual|Get a link to your manual/,
/See what has worked for other communities|Decide how you will decide|Invite your members and change it together/,
);
expect(cards.length).toBeGreaterThan(0);
});
const cards = screen.getAllByText(
/Document how your community|Build an operating manual|Get a link to your manual/,
/See what has worked for other communities|Decide how you will decide|Invite your members and change it together/,
);
expect(cards.length).toBeGreaterThan(0);
@@ -153,11 +153,11 @@ describe("Page Flow Integration", () => {
renderPage();
await waitFor(() => {
expect(screen.getByText("Consensus")).toBeInTheDocument();
expect(screen.getByText("Devolution")).toBeInTheDocument();
});
expect(screen.queryByText("Solidarity Network")).not.toBeInTheDocument();
expect(screen.getByText("Do-ocracy")).toBeInTheDocument();
expect(screen.getByText("Devolution")).toBeInTheDocument();
expect(screen.getAllByText("Consensus").length).toBeGreaterThan(0);
expect(screen.getAllByText("Do-ocracy").length).toBeGreaterThan(0);
expect(screen.getByText("Quadratic Governance")).toBeInTheDocument();
const seeAll = screen.getByRole("link", { name: "See all templates" });
@@ -189,7 +189,7 @@ describe("Page Flow Integration", () => {
expect(headings.length).toBeGreaterThan(4);
const mainContent = screen.getByText(
/Help your community make important decisions/,
/Strong communities agree on four things early/,
);
expect(mainContent).toBeInTheDocument();
});
@@ -220,15 +220,15 @@ describe("Page Flow Integration", () => {
renderPage();
expect(
screen.getByText(/Help your community make important decisions/),
screen.getByText(/Strong communities agree on four things early/),
).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText("How CommunityRule works")).toBeInTheDocument();
expect(screen.getByText("How CommunityRule helps")).toBeInTheDocument();
});
await waitFor(() => {
expect(screen.getByText("Consensus")).toBeInTheDocument();
expect(screen.getByText("Devolution")).toBeInTheDocument();
});
await waitFor(() => {
+15 -18
View File
@@ -38,16 +38,16 @@ describe("User Journey Integration", () => {
renderPageWithFooter();
expect(
screen.getByText(/Help your community make important decisions/),
screen.getByText(/Strong communities agree on four things early/),
).toBeInTheDocument();
const learnLinks = screen.getAllByRole("link", {
name: "Learn how CommunityRule works",
name: "Talk to a facilitator",
});
await user.click(learnLinks[0]);
await waitFor(() => {
expect(screen.getByText("How CommunityRule works")).toBeInTheDocument();
expect(screen.getByText("How CommunityRule helps")).toBeInTheDocument();
});
});
@@ -56,10 +56,10 @@ describe("User Journey Integration", () => {
renderPage();
await waitFor(() => {
expect(screen.getByText("Consensus")).toBeInTheDocument();
expect(screen.getByText("Devolution")).toBeInTheDocument();
});
expect(screen.getByText("Do-ocracy")).toBeInTheDocument();
expect(screen.getByText("Devolution")).toBeInTheDocument();
expect(screen.getAllByText("Consensus").length).toBeGreaterThan(0);
expect(screen.getAllByText("Do-ocracy").length).toBeGreaterThan(0);
expect(screen.getByText("Quadratic Governance")).toBeInTheDocument();
const seeHowLinks = screen.getAllByRole("link", {
@@ -109,15 +109,15 @@ describe("User Journey Integration", () => {
await waitFor(() => {
expect(
screen.getByText("Document how your community makes decisions"),
screen.getByText("See what has worked for other communities"),
).toBeInTheDocument();
});
expect(
screen.getByText("Build an operating manual for a successful community"),
screen.getByText("Decide how you will decide, while things are calm"),
).toBeInTheDocument();
expect(
screen.getByText(
"Get a link to your manual for your group to review and evolve",
"Invite your members and change it together as you grow",
),
).toBeInTheDocument();
@@ -156,7 +156,7 @@ describe("User Journey Integration", () => {
});
expect(
screen.getByText(
"Every group runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
"Every community runs into the same knots. CommunityRule gives you a way through, and a written record of what you decided.",
),
).toBeInTheDocument();
@@ -173,11 +173,8 @@ describe("User Journey Integration", () => {
const partnerLogos = logoImages.filter(
(img) =>
img.alt?.includes("Food Not Bombs") ||
img.alt?.includes("Start COOP") ||
img.alt?.includes("Metagov") ||
img.alt?.includes("Open Civics") ||
img.alt?.includes("Mutual Aid CO") ||
img.alt?.includes("CU Boulder"),
img.alt?.includes("Boulder Street Medics") ||
img.alt?.includes("Mutual Aid Colorado"),
);
expect(partnerLogos.length).toBeGreaterThan(0);
});
@@ -193,11 +190,11 @@ describe("User Journey Integration", () => {
test("user completes the full journey from discovery to action", async () => {
renderPageWithFooter();
expect(screen.getByText("Collaborate")).toBeInTheDocument();
expect(screen.getByText("with clarity")).toBeInTheDocument();
expect(screen.getByText("Build structure")).toBeInTheDocument();
expect(screen.getByText("before you need it")).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText("How CommunityRule works")).toBeInTheDocument();
expect(screen.getByText("How CommunityRule helps")).toBeInTheDocument();
});
await waitFor(() => {
+20 -35
View File
@@ -7,16 +7,16 @@ afterEach(() => {
cleanup();
});
describe("LogoWall Component", () => {
test("renders with default logos", () => {
test("renders the three pilot marks", () => {
render(<LogoWall />);
// Check for default logos
expect(screen.getByAltText("Boulder Street Medics")).toBeInTheDocument();
expect(screen.getByAltText("Food Not Bombs")).toBeInTheDocument();
expect(screen.getByAltText("Start COOP")).toBeInTheDocument();
expect(screen.getByAltText("Metagov")).toBeInTheDocument();
expect(screen.getByAltText("Open Civics")).toBeInTheDocument();
expect(screen.getByAltText("Mutual Aid CO")).toBeInTheDocument();
expect(screen.getByAltText("CU Boulder")).toBeInTheDocument();
expect(screen.getByAltText("Mutual Aid Colorado")).toBeInTheDocument();
expect(screen.queryByAltText("Start COOP")).not.toBeInTheDocument();
expect(screen.queryByAltText("Metagov")).not.toBeInTheDocument();
expect(screen.queryByAltText("Open Civics")).not.toBeInTheDocument();
expect(screen.queryByAltText("CU Boulder")).not.toBeInTheDocument();
});
test("renders with custom logos", () => {
@@ -25,13 +25,11 @@ describe("LogoWall Component", () => {
src: "assets/custom1.png",
alt: "Custom Logo 1",
size: "h-8",
order: "order-1",
},
{
src: "assets/custom2.png",
alt: "Custom Logo 2",
size: "h-10",
order: "order-2",
},
];
@@ -62,19 +60,15 @@ describe("LogoWall Component", () => {
);
});
test("applies responsive grid layout", () => {
test("lays out the three marks in a wrapping row", () => {
render(<LogoWall />);
const grid = document.querySelector(
'[class*="grid grid-cols-2 grid-rows-3"]',
);
expect(grid).toBeInTheDocument();
expect(grid).toHaveClass(
"sm:grid-cols-3",
"sm:grid-rows-2",
"md:flex",
"md:justify-between",
const row = document.querySelector('[class*="flex w-full flex-col"]');
expect(row).toBeInTheDocument();
expect(row).toHaveClass(
"sm:flex-row",
"md:flex-nowrap",
"md:justify-between",
"gap-[38.4px]",
);
});
@@ -97,7 +91,6 @@ describe("LogoWall Component", () => {
test("handles empty logos array", () => {
render(<LogoWall logos={[]} />);
// Should fall back to default logos
expect(screen.getByAltText("Food Not Bombs")).toBeInTheDocument();
});
@@ -112,15 +105,14 @@ describe("LogoWall Component", () => {
).toBe(0);
});
test("renders with proper semantic structure", () => {
test("renders with proper semantic structure and no heading", () => {
render(<LogoWall />);
const section = document.querySelector("section");
expect(section).toBeInTheDocument();
expect(
screen.getByText("Trusted by leading cooperators"),
).toBeInTheDocument();
screen.queryByText("Trusted by leading cooperators"),
).not.toBeInTheDocument();
});
test("renders partner logos at Figma 60% opacity", () => {
@@ -136,22 +128,15 @@ describe("LogoWall Component", () => {
const logos = screen.getAllByRole("img");
logos.forEach((logo) => {
// Next.js Image attributes are not rendered as HTML attributes in JSDOM
// Just verify the images are present
expect(logo).toBeInTheDocument();
});
});
test("prioritizes first two logos", () => {
test("marks the section with the Figma node id", () => {
render(<LogoWall />);
const logos = screen.getAllByRole("img");
const foodNotBombsLogo = logos.find((img) => img.alt === "Food Not Bombs");
const startCoopLogo = logos.find((img) => img.alt === "Start COOP");
// Next.js Image priority attribute is not rendered as HTML attribute in JSDOM
// Just verify the logos are present
expect(foodNotBombsLogo).toBeInTheDocument();
expect(startCoopLogo).toBeInTheDocument();
expect(
document.querySelector('[data-figma-node="18483-18425"]'),
).toBeInTheDocument();
});
});
+3 -1
View File
@@ -90,7 +90,9 @@ describe("RuleStack Component", () => {
test("defaults to home rule stack heading copy when namespace omitted", () => {
render(<RuleStack initialGridEntries={homeFeatured} />);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading.textContent).toMatch(/Popular templates/);
expect(heading.textContent).toMatch(
/Get templates that help your community run smoothly/,
);
});
test("renders four featured governance template cards on the home row", async () => {