Component cleanup

This commit is contained in:
adilallo
2026-04-29 07:20:16 -06:00
parent 252848eba9
commit e6127f1a3f
267 changed files with 2087 additions and 2196 deletions
@@ -3,9 +3,9 @@ import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import CommunityRuleDocument from "../../app/components/sections/CommunityRuleDocument";
import CommunityRule from "../../app/components/type/CommunityRule";
type Props = React.ComponentProps<typeof CommunityRuleDocument>;
type Props = React.ComponentProps<typeof CommunityRule>;
const sampleSections = [
{
@@ -20,8 +20,8 @@ const sampleSections = [
];
const config: ComponentTestSuiteConfig<Props> = {
component: CommunityRuleDocument,
name: "CommunityRuleDocument",
component: CommunityRule,
name: "CommunityRule",
props: {
sections: sampleSections,
} as Props,
@@ -32,6 +32,6 @@ const config: ComponentTestSuiteConfig<Props> = {
},
};
describe("CommunityRuleDocument", () => {
describe("CommunityRule", () => {
componentTestSuite<Props>(config);
});
-23
View File
@@ -1,23 +0,0 @@
import React from "react";
import ContextMenu from "../../app/components/modals/ContextMenu/ContextMenu";
import ContextMenuItem from "../../app/components/modals/ContextMenuItem";
import { componentTestSuite } from "../utils/componentTestSuite";
type ContextMenuProps = React.ComponentProps<typeof ContextMenu>;
componentTestSuite<ContextMenuProps>({
component: ContextMenu,
name: "ContextMenu",
props: {
children: <ContextMenuItem>Item</ContextMenuItem>,
} as ContextMenuProps,
requiredProps: [],
primaryRole: "menu",
testCases: {
renders: true,
accessibility: true,
keyboardNavigation: false,
disabledState: false,
errorState: false,
},
});
-25
View File
@@ -1,25 +0,0 @@
import React from "react";
import ContextMenuItem from "../../app/components/modals/ContextMenuItem";
import { componentTestSuite } from "../utils/componentTestSuite";
type ContextMenuItemProps = React.ComponentProps<typeof ContextMenuItem>;
componentTestSuite<ContextMenuItemProps>({
component: ContextMenuItem,
name: "ContextMenuItem",
props: {
children: "Item",
} as ContextMenuItemProps,
requiredProps: [],
primaryRole: "menuitem",
testCases: {
renders: true,
accessibility: false,
keyboardNavigation: true,
disabledState: true,
errorState: false,
},
states: {
disabledProps: { disabled: true },
},
});
+1 -1
View File
@@ -2,7 +2,7 @@ import React from "react";
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import CreateFlowFooter from "../../app/components/utility/CreateFlowFooter";
import CreateFlowFooter from "../../app/components/navigation/CreateFlowFooter";
import Button from "../../app/components/buttons/Button";
import {
componentTestSuite,
+1 -1
View File
@@ -3,7 +3,7 @@ import { describe, it, expect, vi } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom/vitest";
import CreateFlowTopNav from "../../app/components/utility/CreateFlowTopNav";
import CreateFlowTopNav from "../../app/components/navigation/CreateFlowTopNav";
import {
componentTestSuite,
ComponentTestSuiteConfig,
@@ -1,38 +0,0 @@
import { describe, it, expect } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import DecisionMakingSidebar from "../../app/components/utility/DecisionMakingSidebar";
describe("DecisionMakingSidebar", () => {
const messageBoxItems = [{ id: "1", label: "Consensus" }];
it("renders title and description", () => {
render(
<DecisionMakingSidebar
title="How are decisions made?"
description="Pick approaches for your group."
messageBoxTitle="Select methods"
messageBoxItems={messageBoxItems}
/>,
);
expect(
screen.getByRole("heading", { name: "How are decisions made?" }),
).toBeInTheDocument();
expect(
screen.getByText("Pick approaches for your group."),
).toBeInTheDocument();
});
it("renders InfoMessageBox section", () => {
render(
<DecisionMakingSidebar
title="Decisions"
description="Desc"
messageBoxTitle="Select methods"
messageBoxItems={messageBoxItems}
/>,
);
expect(screen.getByText("Select methods")).toBeInTheDocument();
expect(screen.getByText("Consensus")).toBeInTheDocument();
});
});
+6 -6
View File
@@ -77,7 +77,7 @@ describe("FinalReviewScreen", () => {
).toBeInTheDocument();
});
it("renders RuleCard with fallback title when context has no name", () => {
it("renders Rule with fallback title when context has no name", () => {
render(<FinalReviewScreen />);
expect(screen.getByText(FALLBACK_CARD_TITLE)).toBeInTheDocument();
});
@@ -89,7 +89,7 @@ describe("FinalReviewScreen", () => {
replaceState({
title: "Oak Park Commons",
summary:
"Leftover template or one-line summary — must not appear as the RuleCard description.",
"Leftover template or one-line summary — must not appear as the Rule description.",
});
}, [replaceState]);
return <FinalReviewScreen />;
@@ -100,7 +100,7 @@ describe("FinalReviewScreen", () => {
).not.toBeInTheDocument();
});
it("renders RuleCard title from create flow state", async () => {
it("renders Rule title from create flow state", async () => {
render(
<FinalReviewWithFlowState
title="Oak Park Commons"
@@ -115,7 +115,7 @@ describe("FinalReviewScreen", () => {
).toBeInTheDocument();
});
it("renders RuleCard as a button (card is interactive)", () => {
it("renders Rule as a button (card is interactive)", () => {
render(<FinalReviewScreen />);
const buttons = screen.getAllByRole("button");
expect(buttons.length).toBeGreaterThanOrEqual(1);
@@ -124,7 +124,7 @@ describe("FinalReviewScreen", () => {
).toBe(true);
});
it("renders expanded RuleCard with category labels", () => {
it("renders expanded Rule with category labels", () => {
render(<FinalReviewScreen />);
expect(screen.getByText("Values")).toBeInTheDocument();
expect(screen.getByText("Communication")).toBeInTheDocument();
@@ -143,7 +143,7 @@ describe("FinalReviewScreen", () => {
/**
* Seeds a Customize-from-template style state (method ids + core-value
* snapshot) and asserts the final-review RuleCard renders the resolved
* snapshot) and asserts the final-review Rule renders the resolved
* labels — the fix for "preselected chips don't register on final review".
*/
function FinalReviewWithCustomizeSelections() {
+97 -11
View File
@@ -1,26 +1,112 @@
import { describe } from "vitest";
import React from "react";
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import Icon from "../../app/components/cards/Icon";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import { Icon } from "../../app/components/asset";
type Props = React.ComponentProps<typeof Icon>;
type IconProps = React.ComponentProps<typeof Icon>;
const config: ComponentTestSuiteConfig<Props> = {
const baseProps: IconProps = {
icon: <div data-testid="test-icon">Icon</div>,
title: "Worker's cooperatives",
description:
"Employee-owned businesses often need to clarify how power is shared",
};
const config: ComponentTestSuiteConfig<IconProps> = {
component: Icon,
name: "Icon",
props: {
name: "exclamation",
size: 24,
} as Props,
requiredProps: ["name"],
props: baseProps,
requiredProps: ["icon", "title", "description"],
optionalProps: {
className: "custom-class",
onClick: vi.fn(),
},
primaryRole: "button",
testCases: {
renders: true,
accessibility: true,
keyboardNavigation: true,
disabledState: false,
errorState: false,
},
};
describe("Icon", () => {
componentTestSuite<Props>(config);
componentTestSuite<IconProps>(config);
// Pure presentational; no provider context needed.
describe("Icon (behavioral tests)", () => {
it("calls onClick when clicked", () => {
const handleClick = vi.fn();
render(
<Icon
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
onClick={handleClick}
/>,
);
const card = screen.getByRole("button");
fireEvent.click(card);
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("calls onClick when Enter key is pressed", () => {
const handleClick = vi.fn();
render(
<Icon
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
onClick={handleClick}
/>,
);
const card = screen.getByRole("button");
fireEvent.keyDown(card, { key: "Enter" });
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("calls onClick when Space key is pressed", () => {
const handleClick = vi.fn();
render(
<Icon
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
onClick={handleClick}
/>,
);
const card = screen.getByRole("button");
fireEvent.keyDown(card, { key: " " });
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("renders icon, title, and description", () => {
render(
<Icon
icon={<div data-testid="icon">Icon</div>}
title="Worker's cooperatives"
description="Employee-owned businesses"
/>,
);
expect(screen.getByTestId("icon")).toBeInTheDocument();
expect(screen.getByText("Worker's cooperatives")).toBeInTheDocument();
expect(screen.getByText("Employee-owned businesses")).toBeInTheDocument();
});
it("has proper ARIA label", () => {
render(
<Icon
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
/>,
);
const card = screen.getByRole("button");
expect(card).toHaveAttribute("aria-label", "Test Title: Test Description");
});
});
-112
View File
@@ -1,112 +0,0 @@
import React from "react";
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import IconCard from "../../app/components/cards/IconCard";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
type IconCardProps = React.ComponentProps<typeof IconCard>;
const baseProps: IconCardProps = {
icon: <div data-testid="test-icon">Icon</div>,
title: "Worker's cooperatives",
description:
"Employee-owned businesses often need to clarify how power is shared",
};
const config: ComponentTestSuiteConfig<IconCardProps> = {
component: IconCard,
name: "IconCard",
props: baseProps,
requiredProps: ["icon", "title", "description"],
optionalProps: {
className: "custom-class",
onClick: vi.fn(),
},
primaryRole: "button",
testCases: {
renders: true,
accessibility: true,
keyboardNavigation: true,
disabledState: false,
errorState: false,
},
};
componentTestSuite<IconCardProps>(config);
// Pure presentational; no provider context needed.
describe("IconCard (behavioral tests)", () => {
it("calls onClick when clicked", () => {
const handleClick = vi.fn();
render(
<IconCard
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
onClick={handleClick}
/>,
);
const card = screen.getByRole("button");
fireEvent.click(card);
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("calls onClick when Enter key is pressed", () => {
const handleClick = vi.fn();
render(
<IconCard
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
onClick={handleClick}
/>,
);
const card = screen.getByRole("button");
fireEvent.keyDown(card, { key: "Enter" });
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("calls onClick when Space key is pressed", () => {
const handleClick = vi.fn();
render(
<IconCard
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
onClick={handleClick}
/>,
);
const card = screen.getByRole("button");
fireEvent.keyDown(card, { key: " " });
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("renders icon, title, and description", () => {
render(
<IconCard
icon={<div data-testid="icon">Icon</div>}
title="Worker's cooperatives"
description="Employee-owned businesses"
/>,
);
expect(screen.getByTestId("icon")).toBeInTheDocument();
expect(screen.getByText("Worker's cooperatives")).toBeInTheDocument();
expect(screen.getByText("Employee-owned businesses")).toBeInTheDocument();
});
it("has proper ARIA label", () => {
render(
<IconCard
icon={<div>Icon</div>}
title="Test Title"
description="Test Description"
/>,
);
const card = screen.getByRole("button");
expect(card).toHaveAttribute("aria-label", "Test Title: Test Description");
});
});
+1 -1
View File
@@ -2,7 +2,7 @@ import { describe, it, expect, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import InfoMessageBox from "../../app/components/utility/InfoMessageBox";
import InfoMessageBox from "../../app/components/controls/InfoMessageBox";
describe("InfoMessageBox", () => {
const items = [
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { screen } from "@testing-library/react";
import { renderWithProviders as render } from "../utils/test-utils";
import InputLabel from "../../app/components/utility/InputLabel";
import InputLabel from "../../app/components/type/InputLabel";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
+1 -1
View File
@@ -1,7 +1,7 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import Logo from "../../app/components/asset/logo";
import Logo from "../../app/components/asset/Logo";
import {
componentTestSuite,
ComponentTestSuiteConfig,
+1 -1
View File
@@ -3,7 +3,7 @@ import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import ModalFooter from "../../app/components/utility/ModalFooter";
import ModalFooter from "../../app/components/modals/ModalFooter";
type Props = React.ComponentProps<typeof ModalFooter>;
+1 -1
View File
@@ -3,7 +3,7 @@ import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import ModalHeader from "../../app/components/utility/ModalHeader";
import ModalHeader from "../../app/components/modals/ModalHeader";
type Props = React.ComponentProps<typeof ModalHeader>;
+3 -3
View File
@@ -39,12 +39,12 @@ describe("CommunityReviewScreen", () => {
).toBeInTheDocument();
});
it("renders RuleCard with title fallback when no community name is set", () => {
it("renders Rule with title fallback when no community name is set", () => {
render(<CommunityReviewScreen />);
expect(screen.getByText("Mutual Aid Mondays")).toBeInTheDocument();
});
it("omits the RuleCard description when the user has not entered community context", () => {
it("omits the Rule description when the user has not entered community context", () => {
render(<CommunityReviewScreen />);
expect(
screen.queryByText(
@@ -53,7 +53,7 @@ describe("CommunityReviewScreen", () => {
).not.toBeInTheDocument();
});
it("renders RuleCard as a button (card is interactive)", () => {
it("renders Rule as a button (card is interactive)", () => {
render(<CommunityReviewScreen />);
const buttons = screen.getAllByRole("button");
expect(buttons.length).toBeGreaterThanOrEqual(1);
+30
View File
@@ -0,0 +1,30 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import Section from "../../app/components/type/Section";
import TextBlock from "../../app/components/type/TextBlock";
type Props = React.ComponentProps<typeof Section>;
const config: ComponentTestSuiteConfig<Props> = {
component: Section,
name: "Section",
props: {
categoryName: "Decision making",
showRail: true,
children: (
<TextBlock title="How proposals pass" body="Important decisions require alignment." />
),
} as Props,
requiredProps: ["categoryName", "children"],
testCases: {
renders: true,
accessibility: true,
},
};
describe("Section", () => {
componentTestSuite<Props>(config);
});
+1 -1
View File
@@ -1,5 +1,5 @@
import React from "react";
import SectionHeader from "../../app/components/sections/SectionHeader";
import SectionHeader from "../../app/components/type/SectionHeader";
import { componentTestSuite } from "../utils/componentTestSuite";
type SectionHeaderProps = React.ComponentProps<typeof SectionHeader>;
-22
View File
@@ -1,22 +0,0 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import Separator from "../../app/components/utility/Separator";
type Props = React.ComponentProps<typeof Separator>;
const config: ComponentTestSuiteConfig<Props> = {
component: Separator,
name: "Separator",
props: {} as Props,
testCases: {
renders: true,
accessibility: true,
},
};
describe("Separator", () => {
componentTestSuite<Props>(config);
});
+2 -2
View File
@@ -41,8 +41,8 @@ const config: ComponentTestSuiteConfig<Props> = {
primaryRole: "button",
testCases: {
renders: true,
// RuleCard contains nested interactive elements (chips inside a clickable card)
// which trigger axe's "nested-interactive" rule. Tracked by RuleCard itself.
// Rule contains nested interactive elements (chips inside a clickable card)
// which trigger axe's "nested-interactive" rule. Tracked by Rule itself.
accessibility: false,
},
};
+26
View File
@@ -0,0 +1,26 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import TextBlock from "../../app/components/type/TextBlock";
type Props = React.ComponentProps<typeof TextBlock>;
const config: ComponentTestSuiteConfig<Props> = {
component: TextBlock,
name: "TextBlock",
props: {
title: "Policy title",
body: "Supporting text for the policy.",
} as Props,
requiredProps: ["title"],
testCases: {
renders: true,
accessibility: true,
},
};
describe("TextBlock", () => {
componentTestSuite<Props>(config);
});
@@ -3,7 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom/vitest";
import TopNav from "../../app/components/navigation/TopNav";
import Top from "../../app/components/navigation/Top";
import { renderWithProviders } from "../utils/test-utils";
import { CREATE_FLOW_ANONYMOUS_KEY } from "../../app/(app)/create/utils/anonymousDraftStorage";
import { CORE_VALUE_DETAILS_STORAGE_KEY } from "../../app/(app)/create/utils/coreValueDetailsLocalStorage";
@@ -23,13 +23,13 @@ vi.mock("next/navigation", () => ({
usePathname: () => "/",
}));
type TopNavProps = React.ComponentProps<typeof TopNav>;
type TopProps = React.ComponentProps<typeof Top>;
// Test folderTop=false variant (standard header)
componentTestSuite<TopNavProps>({
component: TopNav,
name: "TopNav (folderTop=false)",
props: { folderTop: false } as TopNavProps,
componentTestSuite<TopProps>({
component: Top,
name: "Top (folderTop=false)",
props: { folderTop: false } as TopProps,
requiredProps: [],
primaryRole: "banner",
testCases: {
@@ -43,10 +43,10 @@ componentTestSuite<TopNavProps>({
// Test folderTop=true variant (home header)
// Note: Accessibility test may fail due to Next.js Script component behavior in test environment
componentTestSuite<TopNavProps>({
component: TopNav,
name: "TopNav (folderTop=true)",
props: { folderTop: true } as TopNavProps,
componentTestSuite<TopProps>({
component: Top,
name: "Top (folderTop=true)",
props: { folderTop: true } as TopProps,
requiredProps: [],
primaryRole: "banner",
testCases: {
@@ -58,7 +58,7 @@ componentTestSuite<TopNavProps>({
},
});
describe('TopNav "Create rule" button', () => {
describe('Top "Create rule" button', () => {
beforeEach(() => {
pushMock.mockReset();
window.localStorage.clear();
@@ -71,7 +71,7 @@ describe('TopNav "Create rule" button', () => {
* Guards against localStorage stickiness on the marketing homepage: hitting
* the top-nav "Create rule" from anywhere outside `/create` must wipe the
* in-flight anonymous draft so the wizard always starts fresh. See
* handleCreateRuleClick in TopNav.container.tsx for the contract.
* handleCreateRuleClick in Top.container.tsx for the contract.
*/
it("clears anonymous draft + core-value-details localStorage before routing to /create", async () => {
window.localStorage.setItem(
@@ -83,9 +83,9 @@ describe('TopNav "Create rule" button', () => {
JSON.stringify({ "1": { meaning: "m", signals: "s" } }),
);
renderWithProviders(<TopNav folderTop={false} />);
renderWithProviders(<Top folderTop={false} />);
// TopNav renders the Create Rule button at three breakpoints (xs/sm/md);
// Top renders the Create Rule button at three breakpoints (xs/sm/md);
// any of them clicking the same handler is the point.
const [btn] = screen.getAllByRole("button", {
name: /create a new rule/i,
+4 -4
View File
@@ -22,10 +22,10 @@ test.describe("Critical User Journeys", () => {
await learnButton.click();
}
// 4. User scrolls to numbered cards section
// Note: SectionHeader shows "How CommunityRule works" on mobile, "How CommunityRule helps" on desktop
// 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 CommunityRule helps")',
'h2:has-text("How CommunityRule works"), h2:has-text("How"), h2:has-text("CommunityRule"), h2:has-text("helps")',
);
await expect(howItWorksHeading).toBeVisible();
@@ -129,7 +129,7 @@ test.describe("Critical User Journeys", () => {
featureSection.locator("text=Conflict resolution"),
).toBeVisible();
// Check feature links - MiniCard components render as <a> tags with href="#..."
// Check feature links - Mini tiles render as <a> tags with href="#..."
// There are 4 feature cards + 1 "Learn more" link = 5 total links
// We check for the specific feature card links
await expect(
+8 -9
View File
@@ -26,16 +26,15 @@ describe("Create flow decision-approaches page", () => {
test("renders sidebar description with add link", () => {
render(<DecisionApproachesScreen />);
const description = screen.getByText((content, element) => {
if (element?.tagName !== "P") return false;
const text = element.textContent ?? "";
return (
text.includes("Select as many as you need") &&
text.includes("add") &&
text.includes("new decision making approaches")
);
const addControl = screen.getByRole("button", {
name: /^add$/,
});
expect(description).toBeInTheDocument();
expect(addControl).toBeInTheDocument();
const description = addControl.parentElement;
expect(description).not.toBeNull();
expect(description?.textContent).toMatch(/Select as many as you need/);
expect(description?.textContent).toMatch(/new decision making approaches/);
});
test("renders message box with title and checkboxes", () => {
+8 -8
View File
@@ -20,7 +20,7 @@ describe("Page", () => {
).toBeGreaterThan(0);
// Wait for dynamically imported components to load
// Check numbered cards section (using getAllByText since there are multiple instances)
// Check CardSteps section (using getAllByText since there are multiple instances)
await waitFor(() => {
expect(
screen.getAllByText("How CommunityRule works").length,
@@ -70,10 +70,10 @@ describe("Page", () => {
).toBeGreaterThan(0);
});
test("renders numbered cards with correct data", async () => {
test("renders CardSteps section with correct data", async () => {
render(<Page />);
// Wait for dynamically imported NumberedCards component to load
// Wait for dynamically imported CardSteps component to load
await waitFor(() => {
expect(
screen.getAllByText("How CommunityRule works").length,
@@ -140,7 +140,7 @@ describe("Page", () => {
// Wait for dynamically imported components to load
// LogoWall - should be present (even if just the component structure)
// NumberedCards
// CardSteps
await waitFor(() => {
expect(
screen.getAllByText("How CommunityRule works").length,
@@ -192,7 +192,7 @@ describe("Page", () => {
).length,
).toBeGreaterThan(0);
// Wait for dynamically imported NumberedCards component
// Wait for dynamically imported CardSteps component
await waitFor(() => {
expect(
screen.getAllByText(
@@ -239,12 +239,12 @@ describe("Page", () => {
);
});
test("renders numbered card items with correct content", async () => {
test("renders CardSteps step tiles with correct content", async () => {
render(<Page />);
// Wait for dynamically imported NumberedCards component
// Wait for dynamically imported CardSteps component
await waitFor(() => {
// Check all three numbered card items (using getAllByText since there are multiple instances)
// Three Step tiles inside CardSteps
expect(
screen.getAllByText("Document how your community makes decisions")
.length,
+6 -7
View File
@@ -70,7 +70,7 @@ describe("Page Flow Integration", () => {
expect(screen.getByAltText("Mutual Aid CO")).toBeInTheDocument();
expect(screen.getByAltText("CU Boulder")).toBeInTheDocument();
// Numbered Cards section - wait for dynamically imported component
// CardSteps section wait for dynamically imported component
await waitFor(() => {
expect(
screen.getByRole("heading", { name: /How CommunityRule works/ }),
@@ -153,10 +153,10 @@ describe("Page Flow Integration", () => {
expect(ctaButton).toBeInTheDocument();
});
test("numbered cards display with correct icons and colors", async () => {
test("CardSteps section shows step tiles with expected icon/color props", async () => {
render(<Page />);
// Wait for dynamically imported NumberedCards component
// Wait for dynamically imported CardSteps component
await waitFor(() => {
const cards = screen.getAllByText(
/Document how your community|Build an operating manual|Get a link to your manual/,
@@ -189,11 +189,10 @@ describe("Page Flow Integration", () => {
const seeAll = screen.getByRole("link", { name: "See all templates" });
expect(seeAll).toHaveAttribute("href", "/templates");
// Check that create rule button is present
const createButton = screen.getByRole("button", {
name: "Create CommunityRule",
const seeHowButton = screen.getByRole("button", {
name: "See how it works",
});
expect(createButton).toBeInTheDocument();
expect(seeHowButton).toBeInTheDocument();
});
test("ask organizer section has proper call-to-action", () => {
+11 -11
View File
@@ -57,7 +57,7 @@ describe("User Journey Integration", () => {
const learnButton = learnButtons[0];
await user.click(learnButton);
// Wait for dynamically imported NumberedCards component
// Wait for dynamically imported CardSteps component
await waitFor(() => {
expect(screen.getByText("How CommunityRule works")).toBeInTheDocument();
});
@@ -77,14 +77,14 @@ describe("User Journey Integration", () => {
expect(screen.getByText("Petition")).toBeInTheDocument();
// User clicks on a governance type to create a rule
const createButtons = screen.getAllByRole("button", {
name: "Create CommunityRule",
const seeHowButtons = screen.getAllByRole("button", {
name: "See how it works",
});
expect(createButtons.length).toBeGreaterThan(0);
expect(seeHowButtons.length).toBeGreaterThan(0);
await user.click(createButtons[0]);
await user.click(seeHowButtons[0]);
// Button should remain interactive
expect(createButtons[0]).toBeInTheDocument();
expect(seeHowButtons[0]).toBeInTheDocument();
});
test("user navigates through the application using header navigation", async () => {
@@ -127,10 +127,10 @@ describe("User Journey Integration", () => {
expect(askLink).toHaveAttribute("href", "#contact");
});
test("user explores the process through numbered cards", async () => {
test("user explores the process through CardSteps", async () => {
render(<Page />);
// Wait for dynamically imported NumberedCards component
// Wait for dynamically imported CardSteps component
await waitFor(() => {
expect(
screen.getByText("Document how your community makes decisions"),
@@ -278,10 +278,10 @@ describe("User Journey Integration", () => {
).toBeInTheDocument();
// 6. User can take action
const createButtons = screen.getAllByRole("button", {
name: "Create CommunityRule",
const seeHowButtons = screen.getAllByRole("button", {
name: "See how it works",
});
expect(createButtons.length).toBeGreaterThan(0);
expect(seeHowButtons.length).toBeGreaterThan(0);
// 7. User can get help if needed
expect(screen.getByText("Still have questions?")).toBeInTheDocument();
+1 -1
View File
@@ -6,7 +6,7 @@ import {
} from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import { vi, describe, test, expect, afterEach } from "vitest";
import CardStack from "../../app/components/utility/CardStack";
import CardStack from "../../app/components/cards/CardStack";
const SAMPLE_CARDS = [
{
@@ -4,14 +4,14 @@ import {
cleanup,
} from "../utils/test-utils";
import { describe, test, expect, afterEach } from "vitest";
import NumberedCards from "../../app/components/sections/NumberedCards";
import CardSteps from "../../app/components/sections/CardSteps";
afterEach(() => {
cleanup();
});
describe("NumberedCards Component", () => {
const mockCards = [
describe("CardSteps Component", () => {
const mockSteps = [
{
text: "Define your community values",
iconShape: "circle",
@@ -29,23 +29,20 @@ describe("NumberedCards Component", () => {
},
];
test("renders with title, subtitle, and cards", () => {
test("renders with title, subtitle, and step cards", () => {
render(
<NumberedCards
<CardSteps
title="How CommunityRule helps"
subtitle="Build better communities step by step"
cards={mockCards}
steps={mockSteps}
/>,
);
// Check for the heading (it contains both mobile and desktop versions)
expect(screen.getByRole("heading")).toBeInTheDocument();
// Check for the subtitle text
expect(
screen.getByText("Build better communities step by step"),
).toBeInTheDocument();
// Check for card content
expect(
screen.getByText("Define your community values"),
).toBeInTheDocument();
@@ -59,55 +56,51 @@ describe("NumberedCards Component", () => {
test("renders SectionHeader component", () => {
render(
<NumberedCards
<CardSteps
title="Test Title"
subtitle="Test Subtitle"
cards={mockCards}
steps={mockSteps}
/>,
);
// Check for the heading (it contains both mobile and desktop versions)
expect(screen.getByRole("heading")).toBeInTheDocument();
// Check for the subtitle text
expect(screen.getByText("Test Subtitle")).toBeInTheDocument();
});
test("renders NumberCard components with correct props", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
test("renders Step tiles with correct props", () => {
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
// Check that NumberCard components receive correct props
expect(screen.getByText("1")).toBeInTheDocument(); // First card number
expect(screen.getByText("2")).toBeInTheDocument(); // Second card number
expect(screen.getByText("3")).toBeInTheDocument(); // Third card number
expect(screen.getByText("1")).toBeInTheDocument();
expect(screen.getByText("2")).toBeInTheDocument();
expect(screen.getByText("3")).toBeInTheDocument();
});
test("renders call-to-action buttons", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
test("renders call-to-action button", () => {
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
expect(
screen.getByRole("button", { name: "Create CommunityRule" }),
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: "See how it works" }),
).toBeInTheDocument();
});
test("applies responsive button visibility", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
test("renders stacked desktop heading lines when provided", () => {
const { container } = render(
<CardSteps
title="Mobile line"
subtitle="Test"
steps={mockSteps}
headingDesktopLines={["How", "CommunityRule", "helps"]}
/>,
);
const createButton = screen.getByRole("button", {
name: "Create CommunityRule",
});
const seeHowButton = screen.getByRole("button", {
name: "See how it works",
});
expect(createButton.closest("div")).toHaveClass("block", "lg:hidden");
expect(seeHowButton.closest("div")).toHaveClass("hidden", "lg:block");
const h2 = container.querySelector("h2");
expect(h2?.textContent).toContain("Mobile line");
expect(h2?.textContent).toContain("CommunityRule");
expect(h2?.textContent).toContain("helps");
});
test("renders with design tokens", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
const section = document.querySelector("section");
expect(section).toHaveClass(
@@ -117,7 +110,7 @@ describe("NumberedCards Component", () => {
});
test("applies responsive grid layout", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
const cardsContainer = document.querySelector(
'[class*="grid grid-cols-1"]',
@@ -127,10 +120,10 @@ describe("NumberedCards Component", () => {
test("renders schema markup", () => {
render(
<NumberedCards
<CardSteps
title="Test Title"
subtitle="Test Description"
cards={mockCards}
steps={mockSteps}
/>,
);
@@ -145,40 +138,37 @@ describe("NumberedCards Component", () => {
});
test("has proper semantic structure", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
const section = document.querySelector("section");
expect(section).toBeInTheDocument();
// Check for proper heading structure
const headings = screen.getAllByRole("heading");
expect(headings.length).toBeGreaterThan(0);
});
test("handles empty cards array", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={[]} />);
test("handles empty steps array", () => {
render(<CardSteps title="Test" subtitle="Test" steps={[]} />);
// Should still render the structure
const section = document.querySelector("section");
expect(section).toBeInTheDocument();
// Should render buttons even without cards
expect(
screen.getByRole("button", { name: "Create CommunityRule" }),
screen.getByRole("button", { name: "See how it works" }),
).toBeInTheDocument();
});
test("applies responsive text alignment", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
test("centers call-to-action region", () => {
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
const buttonContainer = screen
.getByRole("button", { name: "Create CommunityRule" })
const buttonRegion = screen
.getByRole("button", { name: "See how it works" })
.closest("div");
expect(buttonContainer).toHaveClass("block", "lg:hidden");
expect(buttonRegion).toHaveClass("text-center");
});
test("renders with proper spacing", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
const section = document.querySelector("section");
expect(section).toHaveClass(
@@ -188,7 +178,7 @@ describe("NumberedCards Component", () => {
});
test("applies max-width constraint", () => {
render(<NumberedCards title="Test" subtitle="Test" cards={mockCards} />);
render(<CardSteps title="Test" subtitle="Test" steps={mockSteps} />);
const container = document.querySelector(
'[class*="max-w-[var(--spacing-measures-max-width-lg)]"]',
@@ -4,16 +4,16 @@ import {
fireEvent,
} from "../utils/test-utils";
import { describe, it, expect, vi } from "vitest";
import RuleCard from "../../app/components/cards/RuleCard";
import Rule from "../../app/components/cards/Rule";
describe("RuleCard Component", () => {
describe("Rule Component", () => {
const defaultProps = {
title: "Test Rule",
description: "This is a test rule description",
};
it("renders rule card with all required information", () => {
render(<RuleCard {...defaultProps} />);
render(<Rule {...defaultProps} />);
expect(screen.getByText("Test Rule")).toBeInTheDocument();
expect(
@@ -23,14 +23,14 @@ describe("RuleCard Component", () => {
it("renders with custom className", () => {
const customClass = "custom-rule-card";
render(<RuleCard {...defaultProps} className={customClass} />);
render(<Rule {...defaultProps} className={customClass} />);
const card = screen.getByRole("button");
expect(card).toHaveClass(customClass);
});
it("applies default background color", () => {
render(<RuleCard {...defaultProps} />);
render(<Rule {...defaultProps} />);
const card = screen.getByRole("button");
expect(card).toHaveClass("bg-[var(--color-community-teal-100)]");
@@ -38,7 +38,7 @@ describe("RuleCard Component", () => {
it("applies custom background color", () => {
const customBg = "bg-blue-100";
render(<RuleCard {...defaultProps} backgroundColor={customBg} />);
render(<Rule {...defaultProps} backgroundColor={customBg} />);
const card = screen.getByRole("button");
expect(card).toHaveClass(customBg);
@@ -46,14 +46,14 @@ describe("RuleCard Component", () => {
it("renders with icon when provided", () => {
const Icon = () => <span data-testid="icon">🚀</span>;
render(<RuleCard {...defaultProps} icon={<Icon />} />);
render(<Rule {...defaultProps} icon={<Icon />} />);
expect(screen.getByTestId("icon")).toBeInTheDocument();
});
it("handles click events", () => {
const handleClick = vi.fn();
render(<RuleCard {...defaultProps} onClick={handleClick} />);
render(<Rule {...defaultProps} onClick={handleClick} />);
const card = screen.getByRole("button");
fireEvent.click(card);
@@ -63,7 +63,7 @@ describe("RuleCard Component", () => {
it("handles keyboard events", () => {
const handleClick = vi.fn();
render(<RuleCard {...defaultProps} onClick={handleClick} />);
render(<Rule {...defaultProps} onClick={handleClick} />);
const card = screen.getByRole("button");
@@ -77,7 +77,7 @@ describe("RuleCard Component", () => {
});
it("applies hover effects correctly", () => {
render(<RuleCard {...defaultProps} />);
render(<Rule {...defaultProps} />);
const card = screen.getByRole("button");
expect(card).toHaveClass(
@@ -87,7 +87,7 @@ describe("RuleCard Component", () => {
});
it("renders with proper accessibility attributes", () => {
render(<RuleCard {...defaultProps} />);
render(<Rule {...defaultProps} />);
const card = screen.getByRole("button");
expect(card).toHaveAttribute(
@@ -98,7 +98,7 @@ describe("RuleCard Component", () => {
});
it("handles empty description gracefully", () => {
render(<RuleCard {...defaultProps} description="" />);
render(<Rule {...defaultProps} description="" />);
expect(screen.getByText("Test Rule")).toBeInTheDocument();
expect(
@@ -107,14 +107,14 @@ describe("RuleCard Component", () => {
});
it("applies proper sizing for expanded states", () => {
render(<RuleCard {...defaultProps} expanded={true} size="L" />);
render(<Rule {...defaultProps} expanded={true} size="L" />);
const card = screen.getByRole("button");
expect(card).toHaveClass("w-[568px]");
});
it("applies proper accessibility attributes", () => {
render(<RuleCard {...defaultProps} expanded={true} />);
render(<Rule {...defaultProps} expanded={true} />);
const card = screen.getByRole("button");
expect(card).toHaveAttribute("aria-expanded", "true");
@@ -122,13 +122,13 @@ describe("RuleCard Component", () => {
});
it("renders without icon when not provided", () => {
render(<RuleCard {...defaultProps} />);
render(<Rule {...defaultProps} />);
expect(screen.queryByTestId("icon")).not.toBeInTheDocument();
});
it("applies proper border and shadow classes", () => {
render(<RuleCard {...defaultProps} />);
render(<Rule {...defaultProps} />);
const card = screen.getByRole("button");
expect(card).toHaveClass("shadow-[0px_0px_48px_0px_rgba(0,0,0,0.1)]");
@@ -136,7 +136,7 @@ describe("RuleCard Component", () => {
});
it("maintains proper heading structure", () => {
render(<RuleCard {...defaultProps} />);
render(<Rule {...defaultProps} />);
const heading = screen.getByRole("heading", { level: 3 });
expect(heading).toHaveTextContent("Test Rule");
@@ -144,7 +144,7 @@ describe("RuleCard Component", () => {
});
it("applies proper font classes for title", () => {
render(<RuleCard {...defaultProps} size="L" />);
render(<Rule {...defaultProps} size="L" />);
const heading = screen.getByRole("heading", { level: 3 });
// Check for responsive font classes - at 1440px+ it should have font-bricolage-grotesque and font-extrabold
@@ -164,7 +164,7 @@ describe("RuleCard Component", () => {
},
];
render(
<RuleCard {...defaultProps} expanded={true} categories={categories} />,
<Rule {...defaultProps} expanded={true} categories={categories} />,
);
expect(screen.getByText("Values")).toBeInTheDocument();
@@ -172,7 +172,7 @@ describe("RuleCard Component", () => {
it("renders with logo URL", () => {
render(
<RuleCard
<Rule
{...defaultProps}
logoUrl="http://localhost:3845/assets/test.png"
logoAlt="Test Logo"
@@ -184,7 +184,7 @@ describe("RuleCard Component", () => {
});
it("renders with community initials fallback", () => {
render(<RuleCard {...defaultProps} communityInitials="CE" />);
render(<Rule {...defaultProps} communityInitials="CE" />);
expect(screen.getByText("CE")).toBeInTheDocument();
});
+1 -1
View File
@@ -179,7 +179,7 @@ describe("RuleStack Component", () => {
expect(grid).toHaveClass("min-[768px]:grid", "min-[768px]:grid-cols-2");
});
test("renders RuleCard components with catalog surface colors", async () => {
test("renders Rule components with catalog surface colors", async () => {
render(<RuleStack />);
await waitForRuleStackCards();
@@ -4,9 +4,9 @@ import {
fireEvent,
} from "../utils/test-utils";
import { describe, it, expect, vi } from "vitest";
import Card from "../../app/components/cards/Card";
import Selection from "../../app/components/cards/Selection";
describe("Card Component", () => {
describe("Selection Component", () => {
const defaultProps = {
label: "Label",
supportText: "Support text here",
@@ -14,26 +14,26 @@ describe("Card Component", () => {
};
it("renders label and supportText", () => {
render(<Card {...defaultProps} />);
render(<Selection {...defaultProps} />);
expect(screen.getByText("Label")).toBeInTheDocument();
expect(screen.getByText("Support text here")).toBeInTheDocument();
});
it("renders RECOMMENDED pill when recommended is true", () => {
render(<Card {...defaultProps} recommended={true} />);
render(<Selection {...defaultProps} recommended={true} />);
expect(screen.getByText("RECOMMENDED")).toBeInTheDocument();
});
it("does not render RECOMMENDED pill when recommended is false", () => {
render(<Card {...defaultProps} recommended={false} />);
render(<Selection {...defaultProps} recommended={false} />);
expect(screen.queryByText("RECOMMENDED")).not.toBeInTheDocument();
});
it("renders SELECTED pill and inset dashed outline when selected is true", () => {
render(<Card {...defaultProps} selected={true} />);
render(<Selection {...defaultProps} selected={true} />);
expect(screen.getByText("SELECTED")).toBeInTheDocument();
const card = screen.getByRole("button");
@@ -41,14 +41,14 @@ describe("Card Component", () => {
});
it("applies horizontal layout by default", () => {
render(<Card {...defaultProps} />);
render(<Selection {...defaultProps} />);
expect(screen.getByText("Label")).toBeInTheDocument();
expect(screen.getByText("Support text here")).toBeInTheDocument();
});
it("applies vertical layout when orientation is vertical", () => {
render(<Card {...defaultProps} orientation="vertical" />);
render(<Selection {...defaultProps} orientation="vertical" />);
const card = screen.getByRole("button");
expect(card).toHaveClass("flex-row");
@@ -56,7 +56,7 @@ describe("Card Component", () => {
it("handles click events", () => {
const handleClick = vi.fn();
render(<Card {...defaultProps} onClick={handleClick} />);
render(<Selection {...defaultProps} onClick={handleClick} />);
const card = screen.getByRole("button");
fireEvent.click(card);
@@ -66,7 +66,7 @@ describe("Card Component", () => {
it("handles keyboard events", () => {
const handleClick = vi.fn();
render(<Card {...defaultProps} onClick={handleClick} />);
render(<Selection {...defaultProps} onClick={handleClick} />);
const card = screen.getByRole("button");
@@ -79,14 +79,14 @@ describe("Card Component", () => {
it("renders with custom className", () => {
const customClass = "custom-card";
render(<Card {...defaultProps} className={customClass} />);
render(<Selection {...defaultProps} className={customClass} />);
const card = screen.getByRole("button");
expect(card).toHaveClass(customClass);
});
it("renders with proper accessibility attributes", () => {
render(<Card {...defaultProps} />);
render(<Selection {...defaultProps} />);
const card = screen.getByRole("button");
expect(card).toHaveAttribute("aria-label", "Label: Support text here");
@@ -94,7 +94,7 @@ describe("Card Component", () => {
});
it("renders without supportText", () => {
render(<Card label="Label only" orientation="horizontal" />);
render(<Selection label="Label only" orientation="horizontal" />);
expect(screen.getByText("Label only")).toBeInTheDocument();
expect(screen.getByRole("button")).toHaveAttribute(
@@ -1,41 +1,41 @@
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import NumberCard from "../../app/components/cards/NumberCard";
import Step from "../../app/components/cards/Step";
// Pure presentational; no provider context needed.
describe("NumberCard Component", () => {
describe("Step Component", () => {
const defaultProps = {
number: 1,
text: "Test Card Text",
};
it("renders number card with all required information", () => {
render(<NumberCard {...defaultProps} />);
it("renders step with all required information", () => {
render(<Step {...defaultProps} />);
expect(screen.getByText("1")).toBeInTheDocument();
expect(screen.getByText("Test Card Text")).toBeInTheDocument();
});
it("renders with different numbers", () => {
const { rerender } = render(<NumberCard {...defaultProps} number={42} />);
const { rerender } = render(<Step {...defaultProps} number={42} />);
expect(screen.getByText("42")).toBeInTheDocument();
rerender(<NumberCard {...defaultProps} number={999} />);
rerender(<Step {...defaultProps} number={999} />);
expect(screen.getByText("999")).toBeInTheDocument();
});
it("renders with different text content", () => {
const { rerender } = render(
<NumberCard {...defaultProps} text="Different Text" />,
<Step {...defaultProps} text="Different Text" />,
);
expect(screen.getByText("Different Text")).toBeInTheDocument();
rerender(<NumberCard {...defaultProps} text="Another Text" />);
rerender(<Step {...defaultProps} text="Another Text" />);
expect(screen.getByText("Another Text")).toBeInTheDocument();
});
it("applies proper responsive layout classes when size is not specified", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -53,7 +53,7 @@ describe("NumberCard Component", () => {
});
it("applies proper responsive spacing when size is not specified", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -62,7 +62,7 @@ describe("NumberCard Component", () => {
});
it("applies proper responsive gap when size is not specified", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -71,7 +71,7 @@ describe("NumberCard Component", () => {
});
it("applies proper responsive height when size is not specified", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -80,7 +80,7 @@ describe("NumberCard Component", () => {
});
it("applies proper background and shadow", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -92,7 +92,7 @@ describe("NumberCard Component", () => {
});
it("applies proper border radius", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -101,14 +101,14 @@ describe("NumberCard Component", () => {
});
it("renders section number in correct position for responsive mode", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const numberElement = screen.getByText("1");
expect(numberElement).toBeInTheDocument();
});
it("renders text content in correct position for responsive mode", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const textElement = screen.getByText("Test Card Text");
expect(textElement).toBeInTheDocument();
@@ -125,14 +125,14 @@ describe("NumberCard Component", () => {
});
it("applies proper font classes to text", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const textElement = screen.getByText("Test Card Text");
expect(textElement).toHaveClass("font-bricolage-grotesque");
});
it("applies proper text sizing for responsive mode", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const textElement = screen.getByText("Test Card Text");
expect(textElement).toHaveClass(
@@ -147,7 +147,7 @@ describe("NumberCard Component", () => {
});
it("applies proper text color", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const textElement = screen.getByText("Test Card Text");
expect(textElement).toHaveClass("text-[#141414]");
@@ -155,14 +155,14 @@ describe("NumberCard Component", () => {
it("handles long text content gracefully", () => {
const longText =
"This is a very long text that should wrap properly and not break the layout of the number card component";
render(<NumberCard {...defaultProps} text={longText} />);
"This is a very long text that should wrap properly and not break the layout of the Step component";
render(<Step {...defaultProps} text={longText} />);
expect(screen.getByText(longText)).toBeInTheDocument();
});
it("maintains proper responsive behavior when size is not specified", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -192,7 +192,7 @@ describe("NumberCard Component", () => {
});
it("renders with proper flex layout", () => {
render(<NumberCard {...defaultProps} />);
render(<Step {...defaultProps} />);
const card = screen
.getByText("Test Card Text")
@@ -201,7 +201,7 @@ describe("NumberCard Component", () => {
});
it("applies Small size variant correctly", () => {
render(<NumberCard {...defaultProps} size="small" />);
render(<Step {...defaultProps} size="small" />);
// For Small size, text is directly in card div (no wrapper), so use closest("div")
const card = screen.getByText("Test Card Text").closest("div");
@@ -220,7 +220,7 @@ describe("NumberCard Component", () => {
});
it("applies Medium size variant correctly", () => {
render(<NumberCard {...defaultProps} size="medium" />);
render(<Step {...defaultProps} size="medium" />);
const card = screen
.getByText("Test Card Text")
@@ -238,7 +238,7 @@ describe("NumberCard Component", () => {
});
it("applies Large size variant correctly", () => {
render(<NumberCard {...defaultProps} size="large" />);
render(<Step {...defaultProps} size="large" />);
const card = screen
.getByText("Test Card Text")
@@ -258,7 +258,7 @@ describe("NumberCard Component", () => {
});
it("applies XLarge size variant correctly", () => {
render(<NumberCard {...defaultProps} size="xlarge" />);
render(<Step {...defaultProps} size="xlarge" />);
const card = screen
.getByText("Test Card Text")
+33
View File
@@ -209,6 +209,39 @@ describe("parseDocumentSectionsForDisplay", () => {
};
expect(parseDocumentSectionsForDisplay(doc)).toEqual(doc.sections);
});
it("accepts entries with labeled blocks and empty body", () => {
const doc = {
sections: [
{
categoryName: "Membership",
entries: [
{
title: "Open membership",
body: "",
blocks: [
{ label: "Eligibility", body: "Anyone may join." },
{ label: "Process", body: "Sign the sheet." },
],
},
],
},
],
};
expect(parseDocumentSectionsForDisplay(doc)).toEqual(doc.sections);
});
it("still parses entries with empty body and no blocks", () => {
const doc = {
sections: [
{
categoryName: "Values",
entries: [{ title: "Consensus", body: "" }],
},
],
};
expect(parseDocumentSectionsForDisplay(doc)).toEqual(doc.sections);
});
});
describe("parseSectionsFromCreateFlowState", () => {
+1 -1
View File
@@ -7,7 +7,7 @@ import {
} from "../../lib/create/templateReviewMapping";
describe("templateReviewMapping", () => {
it("maps body sections to RuleCard categories", () => {
it("maps body sections to Rule categories", () => {
const body = {
sections: [
{
+1 -1
View File
@@ -6,7 +6,7 @@ import { CreateFlowProvider } from "../../app/(app)/create/context/CreateFlowCon
import messages from "../../messages/en/index";
/**
* Custom render function: MessagesProvider, AuthModalProvider (TopNav login), CreateFlowProvider.
* Custom render function: MessagesProvider, AuthModalProvider (`Top` login), CreateFlowProvider.
*/
export function renderWithProviders(
ui: ReactElement,