App reorganization

This commit is contained in:
adilallo
2026-04-18 14:12:49 -06:00
parent f866d11ff8
commit e9dab04b34
288 changed files with 2698 additions and 5029 deletions
@@ -3,7 +3,7 @@ import { describe, it, expect, vi } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom/vitest";
import ApplicableScopeField from "../../app/create/components/ApplicableScopeField";
import ApplicableScopeField from "../../app/(app)/create/components/ApplicableScopeField";
import { componentTestSuite } from "../utils/componentTestSuite";
import { renderWithProviders } from "../utils/test-utils";
+1
View File
@@ -39,6 +39,7 @@ const config: ComponentTestSuiteConfig<ButtonProps> = {
componentTestSuite<ButtonProps>(config);
// Pure presentational; no provider context needed.
describe("Button (behavioral tests)", () => {
it("calls onClick when clicked", async () => {
const user = userEvent.setup();
+33
View File
@@ -0,0 +1,33 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import Chip from "../../app/components/controls/Chip";
type Props = React.ComponentProps<typeof Chip>;
const config: ComponentTestSuiteConfig<Props> = {
component: Chip,
name: "Chip",
props: {
label: "Worker cooperative",
state: "unselected",
palette: "default",
size: "m",
} as Props,
primaryRole: "button",
testCases: {
renders: true,
accessibility: true,
keyboardNavigation: true,
disabledState: true,
},
states: {
disabledProps: { disabled: true, state: "disabled" },
},
};
describe("Chip", () => {
componentTestSuite<Props>(config);
});
@@ -0,0 +1,37 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import CommunityRuleDocument from "../../app/components/sections/CommunityRuleDocument";
type Props = React.ComponentProps<typeof CommunityRuleDocument>;
const sampleSections = [
{
categoryName: "Decision making",
entries: [
{
title: "How proposals pass",
body: "Important decisions require unanimous agreement.",
},
],
},
];
const config: ComponentTestSuiteConfig<Props> = {
component: CommunityRuleDocument,
name: "CommunityRuleDocument",
props: {
sections: sampleSections,
} as Props,
requiredProps: ["sections"],
testCases: {
renders: true,
accessibility: true,
},
};
describe("CommunityRuleDocument", () => {
componentTestSuite<Props>(config);
});
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import { CompletedScreen } from "../../app/create/screens/completed/CompletedScreen";
import { CompletedScreen } from "../../app/(app)/create/screens/completed/CompletedScreen";
describe("CompletedScreen", () => {
it("renders without crashing", () => {
@@ -2,7 +2,7 @@ import { describe, it, expect } 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 { ConfirmStakeholdersScreen } from "../../app/create/screens/select/ConfirmStakeholdersScreen";
import { ConfirmStakeholdersScreen } from "../../app/(app)/create/screens/select/ConfirmStakeholdersScreen";
describe("ConfirmStakeholdersScreen", () => {
it("renders title and description", () => {
+1
View File
@@ -43,6 +43,7 @@ const mockPost: BlogPost = {
lastModified: new Date("2025-04-15"),
};
// Pure presentational; no provider context needed.
describe("ContentBanner", () => {
it("renders without crashing", () => {
render(<ContentBanner post={mockPost} />);
+2 -2
View File
@@ -1,6 +1,6 @@
import React from "react";
import ContextMenu from "../../app/components/ContextMenu/ContextMenu";
import ContextMenuItem from "../../app/components/ContextMenu/ContextMenuItem";
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>;
+1 -1
View File
@@ -1,5 +1,5 @@
import React from "react";
import ContextMenuItem from "../../app/components/ContextMenu/ContextMenuItem";
import ContextMenuItem from "../../app/components/modals/ContextMenuItem";
import { componentTestSuite } from "../utils/componentTestSuite";
type ContextMenuItemProps = React.ComponentProps<typeof ContextMenuItem>;
@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
import { screen, fireEvent, waitFor, within } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import { renderWithProviders } from "../utils/test-utils";
import { CoreValuesSelectScreen } from "../../app/create/screens/select/CoreValuesSelectScreen";
import { CoreValuesSelectScreen } from "../../app/(app)/create/screens/select/CoreValuesSelectScreen";
describe("CoreValuesSelectScreen", () => {
beforeEach(() => {
@@ -35,6 +35,7 @@ const config: ComponentTestSuiteConfig<CreateFlowFooterProps> = {
componentTestSuite<CreateFlowFooterProps>(config);
// Pure presentational; no provider context needed (no useMessages/useAuthModal/useCreateFlow consumers).
describe("CreateFlowFooter (behavioral tests)", () => {
it("renders Back button", () => {
render(<CreateFlowFooter />);
+2 -2
View File
@@ -6,8 +6,8 @@ import {
waitFor,
} from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import { FinalReviewScreen } from "../../app/create/screens/review/FinalReviewScreen";
import { useCreateFlow } from "../../app/create/context/CreateFlowContext";
import { FinalReviewScreen } from "../../app/(app)/create/screens/review/FinalReviewScreen";
import { useCreateFlow } from "../../app/(app)/create/context/CreateFlowContext";
const FALLBACK_CARD_TITLE = "Your community";
const FALLBACK_CARD_DESCRIPTION_SNIPPET =
@@ -0,0 +1,28 @@
import { describe, vi } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import { GovernanceTemplateGrid } from "../../app/components/sections/GovernanceTemplateGrid";
import { GOVERNANCE_TEMPLATE_CATALOG } from "../../lib/templates/governanceTemplateCatalog";
type Props = React.ComponentProps<typeof GovernanceTemplateGrid>;
const config: ComponentTestSuiteConfig<Props> = {
component: GovernanceTemplateGrid,
name: "GovernanceTemplateGrid",
props: {
entries: GOVERNANCE_TEMPLATE_CATALOG.slice(0, 2),
onTemplateClick: vi.fn(),
} as Props,
requiredProps: ["entries", "onTemplateClick"],
primaryRole: "button",
testCases: {
renders: true,
accessibility: true,
},
};
describe("GovernanceTemplateGrid", () => {
componentTestSuite<Props>(config);
});
+3 -2
View File
@@ -36,6 +36,7 @@ const config: ComponentTestSuiteConfig<HeaderLockupProps> = {
componentTestSuite<HeaderLockupProps>(config);
// Pure presentational; no provider context needed.
describe("HeaderLockup (behavioral tests)", () => {
it("renders title", () => {
render(<HeaderLockup title="Test Title" />);
@@ -84,8 +85,8 @@ describe("HeaderLockup (behavioral tests)", () => {
expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
});
it("accepts PascalCase props", () => {
render(<HeaderLockup title="Test Title" justification="Left" size="L" />);
it("accepts justification and size props", () => {
render(<HeaderLockup title="Test Title" justification="left" size="L" />);
expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
});
});
+26
View File
@@ -0,0 +1,26 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import { Icon } from "../../app/components/asset";
type Props = React.ComponentProps<typeof Icon>;
const config: ComponentTestSuiteConfig<Props> = {
component: Icon,
name: "Icon",
props: {
name: "exclamation",
size: 24,
} as Props,
requiredProps: ["name"],
testCases: {
renders: true,
accessibility: true,
},
};
describe("Icon", () => {
componentTestSuite<Props>(config);
});
+1
View File
@@ -38,6 +38,7 @@ const config: ComponentTestSuiteConfig<IconCardProps> = {
componentTestSuite<IconCardProps>(config);
// Pure presentational; no provider context needed.
describe("IconCard (behavioral tests)", () => {
it("calls onClick when clicked", () => {
const handleClick = vi.fn();
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import { InformationalScreen } from "../../app/create/screens/informational/InformationalScreen";
import { InformationalScreen } from "../../app/(app)/create/screens/informational/InformationalScreen";
describe("InformationalScreen", () => {
it("renders without crashing", () => {
+12 -12
View File
@@ -17,16 +17,16 @@ const config: ComponentTestSuiteConfig<Props> = {
helpIcon: false,
asterisk: false,
helperText: false,
size: "S",
palette: "Default",
size: "s",
palette: "default",
} as Props,
requiredProps: ["label"],
optionalProps: {
helpIcon: true,
asterisk: true,
helperText: true,
size: "M",
palette: "Inverse",
size: "m",
palette: "inverse",
},
primaryRole: undefined, // InputLabel is not directly interactive
testCases: {
@@ -67,28 +67,28 @@ describe("InputLabel behaviour specifics", () => {
expect(screen.getByText("Custom helper")).toBeInTheDocument();
});
it("applies size S styling", () => {
render(<InputLabel label="Test Label" size="S" />);
it("applies size s styling", () => {
render(<InputLabel label="Test Label" size="s" />);
const label = screen.getByText("Test Label");
expect(label).toHaveClass("text-[length:var(--sizing-350,14px)]");
});
it("applies size M styling", () => {
render(<InputLabel label="Test Label" size="M" />);
it("applies size m styling", () => {
render(<InputLabel label="Test Label" size="m" />);
const label = screen.getByText("Test Label");
expect(label).toHaveClass("text-[length:var(--sizing-400,16px)]");
});
it("applies Default palette styling", () => {
render(<InputLabel label="Test Label" palette="Default" />);
it("applies default palette styling", () => {
render(<InputLabel label="Test Label" palette="default" />);
const label = screen.getByText("Test Label");
expect(label).toHaveClass(
"text-[color:var(--color-content-default-secondary,#d2d2d2)]",
);
});
it("applies Inverse palette styling", () => {
render(<InputLabel label="Test Label" palette="Inverse" />);
it("applies inverse palette styling", () => {
render(<InputLabel label="Test Label" palette="inverse" />);
const label = screen.getByText("Test Label");
expect(label).toHaveClass(
"text-[color:var(--color-content-inverse-secondary,#1f1f1f)]",
@@ -0,0 +1,31 @@
import { describe, vi } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import InputWithCounter from "../../app/components/controls/InputWithCounter";
type Props = React.ComponentProps<typeof InputWithCounter>;
const config: ComponentTestSuiteConfig<Props> = {
component: InputWithCounter,
name: "InputWithCounter",
props: {
label: "Community name",
placeholder: "Enter a name",
value: "",
onChange: vi.fn(),
maxLength: 50,
showHelpIcon: false,
} as Props,
requiredProps: ["value", "onChange", "maxLength"],
primaryRole: "textbox",
testCases: {
renders: true,
accessibility: true,
},
};
describe("InputWithCounter", () => {
componentTestSuite<Props>(config);
});
@@ -0,0 +1,23 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import LanguageSwitcher from "../../app/components/localization/LanguageSwitcher";
type Props = React.ComponentProps<typeof LanguageSwitcher>;
const config: ComponentTestSuiteConfig<Props> = {
component: LanguageSwitcher,
name: "LanguageSwitcher",
props: {} as Props,
primaryRole: "combobox",
testCases: {
renders: true,
accessibility: true,
},
};
describe("LanguageSwitcher", () => {
componentTestSuite<Props>(config);
});
+3 -3
View File
@@ -32,10 +32,10 @@ vi.mock("../../lib/create/api", () => ({
requestMagicLink: vi.fn(),
}));
vi.mock("../../app/create/utils/anonymousDraftStorage", async (importOriginal) => {
vi.mock("../../app/(app)/create/utils/anonymousDraftStorage", async (importOriginal) => {
const actual =
await importOriginal<
typeof import("../../app/create/utils/anonymousDraftStorage")
typeof import("../../app/(app)/create/utils/anonymousDraftStorage")
>();
return {
...actual,
@@ -44,7 +44,7 @@ vi.mock("../../app/create/utils/anonymousDraftStorage", async (importOriginal) =
});
import { requestMagicLink } from "../../lib/create/api";
import { setTransferPendingFlag } from "../../app/create/utils/anonymousDraftStorage";
import { setTransferPendingFlag } from "../../app/(app)/create/utils/anonymousDraftStorage";
function renderLoginForm() {
return renderWithProviders(
+1
View File
@@ -27,6 +27,7 @@ const config: ComponentTestSuiteConfig<LogoProps> = {
componentTestSuite<LogoProps>(config);
// Pure presentational; no provider context needed.
describe("Logo (behavioral tests)", () => {
it("renders as a link to home", () => {
render(<Logo />);
+30
View File
@@ -0,0 +1,30 @@
import { describe, vi } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import ModalFooter from "../../app/components/utility/ModalFooter";
type Props = React.ComponentProps<typeof ModalFooter>;
const config: ComponentTestSuiteConfig<Props> = {
component: ModalFooter,
name: "ModalFooter",
props: {
showBackButton: true,
showNextButton: true,
onBack: vi.fn(),
onNext: vi.fn(),
currentStep: 2,
totalSteps: 4,
} as Props,
primaryRole: "button",
testCases: {
renders: true,
accessibility: true,
},
};
describe("ModalFooter", () => {
componentTestSuite<Props>(config);
});
+28
View File
@@ -0,0 +1,28 @@
import { describe, vi } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import ModalHeader from "../../app/components/utility/ModalHeader";
type Props = React.ComponentProps<typeof ModalHeader>;
const config: ComponentTestSuiteConfig<Props> = {
component: ModalHeader,
name: "ModalHeader",
props: {
showCloseButton: true,
showMoreOptionsButton: true,
onClose: vi.fn(),
onMoreOptions: vi.fn(),
} as Props,
primaryRole: "button",
testCases: {
renders: true,
accessibility: true,
},
};
describe("ModalHeader", () => {
componentTestSuite<Props>(config);
});
+1 -1
View File
@@ -3,7 +3,7 @@ import { describe, it, expect, vi } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom/vitest";
import ModalTextAreaField from "../../app/create/components/ModalTextAreaField";
import ModalTextAreaField from "../../app/(app)/create/components/ModalTextAreaField";
import { componentTestSuite } from "../utils/componentTestSuite";
import { renderWithProviders } from "../utils/test-utils";
+8 -8
View File
@@ -11,8 +11,8 @@ import {
type Props = React.ComponentProps<typeof MultiSelect>;
const defaultChipOptions = [
{ id: "1", label: "Option 1", state: "Unselected" as const },
{ id: "2", label: "Option 2", state: "Selected" as const },
{ id: "1", label: "Option 1", state: "unselected" as const },
{ id: "2", label: "Option 2", state: "selected" as const },
];
const config: ComponentTestSuiteConfig<Props> = {
@@ -21,8 +21,8 @@ const config: ComponentTestSuiteConfig<Props> = {
props: {
label: "Test Label",
showHelpIcon: false,
size: "S",
palette: "Default",
size: "s",
palette: "default",
options: defaultChipOptions,
addButton: true,
addButtonText: "",
@@ -31,8 +31,8 @@ const config: ComponentTestSuiteConfig<Props> = {
optionalProps: {
label: "Optional Label",
showHelpIcon: true,
size: "M",
palette: "Inverse",
size: "m",
palette: "inverse",
onChipClick: vi.fn(),
onAddClick: vi.fn(),
addButton: false,
@@ -144,7 +144,7 @@ describe("MultiSelect behaviour specifics", () => {
it("handles custom chip confirm", async () => {
const handleConfirm = vi.fn();
const customOptions = [
{ id: "custom-1", label: "", state: "Custom" as const },
{ id: "custom-1", label: "", state: "custom" as const },
];
render(
<MultiSelect
@@ -169,7 +169,7 @@ describe("MultiSelect behaviour specifics", () => {
it("handles custom chip close", async () => {
const handleClose = vi.fn();
const customOptions = [
{ id: "custom-1", label: "", state: "Custom" as const },
{ id: "custom-1", label: "", state: "custom" as const },
];
render(
<MultiSelect options={customOptions} onCustomChipClose={handleClose} />,
+29
View File
@@ -0,0 +1,29 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import NavigationItem from "../../app/components/navigation/NavigationItem";
type Props = React.ComponentProps<typeof NavigationItem>;
const config: ComponentTestSuiteConfig<Props> = {
component: NavigationItem,
name: "NavigationItem",
props: {
children: "Templates",
href: "#",
variant: "default",
size: "default",
} as Props,
primaryRole: "link",
testCases: {
renders: true,
accessibility: true,
keyboardNavigation: true,
},
};
describe("NavigationItem", () => {
componentTestSuite<Props>(config);
});
+1
View File
@@ -49,6 +49,7 @@ const config: ComponentTestSuiteConfig<NumberedListProps> = {
componentTestSuite<NumberedListProps>(config);
// Pure presentational; no provider context needed.
describe("NumberedList (behavioral tests)", () => {
it("renders all items", () => {
render(<NumberedList items={mockItems} />);
+1
View File
@@ -36,6 +36,7 @@ const config: ComponentTestSuiteConfig<ProportionBarProps> = {
componentTestSuite<ProportionBarProps>(config);
// Pure presentational; no provider context needed.
describe("ProportionBar (behavioral tests)", () => {
it("renders proportion bar with correct progress value", () => {
render(<ProportionBar progress="2-1" />);
@@ -63,6 +63,7 @@ const mockPosts: BlogPost[] = [
},
];
// Pure presentational; no provider context needed (mocked thumbnail + useIsMobile).
describe("RelatedArticles", () => {
it("renders without crashing", () => {
render(
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import { CommunityReviewScreen } from "../../app/create/screens/review/CommunityReviewScreen";
import { CommunityReviewScreen } from "../../app/(app)/create/screens/review/CommunityReviewScreen";
describe("CommunityReviewScreen", () => {
it("renders without crashing", () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import { CommunitySizeSelectScreen } from "../../app/create/screens/select/CommunitySizeSelectScreen";
import { CommunitySizeSelectScreen } from "../../app/(app)/create/screens/select/CommunitySizeSelectScreen";
describe("CommunitySizeSelectScreen", () => {
it("renders HeaderLockup title", () => {
+22
View File
@@ -0,0 +1,22 @@
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);
});
+1
View File
@@ -37,6 +37,7 @@ const config: ComponentTestSuiteConfig<StepperProps> = {
componentTestSuite<StepperProps>(config);
// Pure presentational; no provider context needed.
describe("Stepper (behavioral tests)", () => {
it("renders with correct number of steps", () => {
render(<Stepper active={3} totalSteps={5} />);
@@ -0,0 +1,52 @@
import { describe } from "vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import { TemplateReviewCard } from "../../app/components/cards/TemplateReviewCard";
type Props = React.ComponentProps<typeof TemplateReviewCard>;
const sampleTemplate = {
id: "tmpl-1",
slug: "consensus",
title: "Consensus",
category: null,
description:
"Important decisions require unanimous agreement. Proposals pass only if no serious objections remain.",
body: {
sections: [
{
categoryName: "Decision making",
entries: [
{
title: "How proposals pass",
body: "Unanimous agreement is required.",
},
],
},
],
},
sortOrder: 1,
featured: true,
};
const config: ComponentTestSuiteConfig<Props> = {
component: TemplateReviewCard,
name: "TemplateReviewCard",
props: {
template: sampleTemplate,
size: "L",
} as 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.
accessibility: false,
},
};
describe("TemplateReviewCard", () => {
componentTestSuite<Props>(config);
});
+1 -5
View File
@@ -32,6 +32,7 @@ componentTestSuite<TextInputProps>({
},
});
// Pure presentational; no provider context needed.
describe("TextInput (size tests)", () => {
it("renders with medium size by default", () => {
const { container } = render(<TextInput label="Test" inputSize="medium" />);
@@ -45,9 +46,4 @@ describe("TextInput (size tests)", () => {
expect(input).toHaveClass("h-[32px]");
});
it("accepts PascalCase size prop", () => {
const { container } = render(<TextInput label="Test" inputSize="Small" />);
const input = container.querySelector("input");
expect(input).toHaveClass("h-[32px]");
});
});
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import { CreateFlowTextFieldScreen } from "../../app/create/screens/text/CreateFlowTextFieldScreen";
import { CreateFlowTextFieldScreen } from "../../app/(app)/create/screens/text/CreateFlowTextFieldScreen";
describe("CreateFlowTextFieldScreen (community name)", () => {
it("renders main heading", () => {
+1
View File
@@ -30,6 +30,7 @@ componentTestSuite<TooltipProps>({
},
});
// Pure presentational; no provider context needed.
describe("Tooltip (behavioral tests)", () => {
it("shows tooltip on hover", async () => {
const user = userEvent.setup();
+1
View File
@@ -30,6 +30,7 @@ componentTestSuite<UploadProps>({
},
});
// Pure presentational; no provider context needed.
describe("Upload (behavioral tests)", () => {
it("renders with active state by default", () => {
render(<Upload label="Upload" />);
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { renderWithProviders as render, screen } from "../utils/test-utils";
import "@testing-library/jest-dom/vitest";
import { CommunityUploadScreen } from "../../app/create/screens/upload/CommunityUploadScreen";
import { CommunityUploadScreen } from "../../app/(app)/create/screens/upload/CommunityUploadScreen";
describe("CommunityUploadScreen", () => {
it("renders HeaderLockup", () => {
@@ -31,10 +31,10 @@ vi.mock("../../lib/create/api", () => ({
requestMagicLink: vi.fn(),
}));
vi.mock("../../app/create/utils/anonymousDraftStorage", async (importOriginal) => {
vi.mock("../../app/(app)/create/utils/anonymousDraftStorage", async (importOriginal) => {
const actual =
await importOriginal<
typeof import("../../app/create/utils/anonymousDraftStorage")
typeof import("../../app/(app)/create/utils/anonymousDraftStorage")
>();
return {
...actual,
@@ -43,7 +43,7 @@ vi.mock("../../app/create/utils/anonymousDraftStorage", async (importOriginal) =
});
import { requestMagicLink } from "../../lib/create/api";
import { setTransferPendingFlag } from "../../app/create/utils/anonymousDraftStorage";
import { setTransferPendingFlag } from "../../app/(app)/create/utils/anonymousDraftStorage";
function LoginTrigger() {
const { openLogin, closeLogin } = useAuthModal();
+1 -1
View File
@@ -6,7 +6,7 @@ import {
} from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import { describe, test, expect, afterEach } from "vitest";
import { CommunicationMethodsScreen } from "../../app/create/screens/card/CommunicationMethodsScreen";
import { CommunicationMethodsScreen } from "../../app/(app)/create/screens/card/CommunicationMethodsScreen";
afterEach(() => {
cleanup();
+1 -1
View File
@@ -6,7 +6,7 @@ import {
} from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import { describe, test, expect, afterEach } from "vitest";
import { DecisionApproachesScreen } from "../../app/create/screens/right-rail/DecisionApproachesScreen";
import { DecisionApproachesScreen } from "../../app/(app)/create/screens/right-rail/DecisionApproachesScreen";
afterEach(() => {
cleanup();
+1
View File
@@ -24,6 +24,7 @@ const mockPost = {
},
};
// Pure presentational; no provider context needed.
describe("ContentContainer", () => {
it("renders with default props", () => {
render(<ContentContainer post={mockPost} />);
@@ -35,6 +35,7 @@ const mockPost = {
},
};
// Pure presentational; no provider context needed.
describe("ContentThumbnailTemplate", () => {
describe("Vertical Variant", () => {
it("should render vertical variant with responsive dimensions", () => {
+37 -19
View File
@@ -1,5 +1,7 @@
import { describe, test, expect, vi } from "vitest";
import RootLayout from "../../app/layout";
import MarketingLayout from "../../app/(marketing)/layout";
import AppLayout from "../../app/(app)/layout";
// Mock the font imports since they're Next.js specific
vi.mock("next/font/google", () => ({
@@ -70,30 +72,46 @@ describe("RootLayout", () => {
expect(container).toBeTruthy();
});
test("renders main content area", () => {
const testContent = "Test content";
const tree = RootLayout({ children: <div>{testContent}</div> });
const main = findDescendant(
tree,
(n) => n.type === "main" && n.props?.className?.includes("flex-1"),
);
expect(main).toBeTruthy();
const childText = findDescendant(
main,
(n) => typeof n === "string" && n.includes(testContent),
);
expect(childText).toBeTruthy();
});
test("renders children content correctly", () => {
test("renders children directly inside the flex container (no <main> at root)", () => {
const testContent = "This is test content";
const tree = RootLayout({ children: <div>{testContent}</div> });
const main = findDescendant(tree, (n) => n.type === "main");
expect(findDescendant(tree, (n) => n?.type === "main")).toBeNull();
const childText = findDescendant(
main,
tree,
(n) => typeof n === "string" && n.includes(testContent),
);
expect(childText).toBeTruthy();
});
});
describe("Group layouts (chrome composition)", () => {
test("MarketingLayout wraps children in <main flex-1> and appends Footer", () => {
const tree = MarketingLayout({ children: <div>marketing-child</div> });
const main = findDescendant(
tree,
(n) => n?.type === "main" && n.props?.className?.includes("flex-1"),
);
expect(main).toBeTruthy();
expect(
findDescendant(main, (n) => typeof n === "string" && n.includes("marketing-child")),
).toBeTruthy();
// Footer is loaded via next/dynamic — it appears as a render prop component
// sibling to <main>. Verify the layout returns more than just <main>.
const childrenArr = Array.isArray(tree.props.children)
? tree.props.children
: [tree.props.children];
expect(childrenArr.length).toBeGreaterThan(1);
});
test("AppLayout wraps children in <main flex-1> with no footer", () => {
const tree = AppLayout({ children: <div>app-child</div> });
expect(tree.type).toBe("main");
expect(tree.props.className).toContain("flex-1");
expect(
findDescendant(tree, (n) => typeof n === "string" && n.includes("app-child")),
).toBeTruthy();
});
});
+1
View File
@@ -6,6 +6,7 @@ afterEach(() => {
cleanup();
});
// Pure presentational; no provider context needed.
describe("LogoWall Component", () => {
test("renders with default logos", () => {
render(<LogoWall />);
+5 -4
View File
@@ -2,6 +2,7 @@ import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import NumberCard from "../../app/components/cards/NumberCard";
// Pure presentational; no provider context needed.
describe("NumberCard Component", () => {
const defaultProps = {
number: 1,
@@ -200,7 +201,7 @@ describe("NumberCard Component", () => {
});
it("applies Small size variant correctly", () => {
render(<NumberCard {...defaultProps} size="Small" />);
render(<NumberCard {...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");
@@ -219,7 +220,7 @@ describe("NumberCard Component", () => {
});
it("applies Medium size variant correctly", () => {
render(<NumberCard {...defaultProps} size="Medium" />);
render(<NumberCard {...defaultProps} size="medium" />);
const card = screen
.getByText("Test Card Text")
@@ -237,7 +238,7 @@ describe("NumberCard Component", () => {
});
it("applies Large size variant correctly", () => {
render(<NumberCard {...defaultProps} size="Large" />);
render(<NumberCard {...defaultProps} size="large" />);
const card = screen
.getByText("Test Card Text")
@@ -257,7 +258,7 @@ describe("NumberCard Component", () => {
});
it("applies XLarge size variant correctly", () => {
render(<NumberCard {...defaultProps} size="XLarge" />);
render(<NumberCard {...defaultProps} size="xlarge" />);
const card = screen
.getByText("Test Card Text")
+1 -1
View File
@@ -159,7 +159,7 @@ describe("RuleCard Component", () => {
{
name: "Values",
chipOptions: [
{ id: "v1", label: "Consciousness", state: "Unselected" },
{ id: "v1", label: "Consciousness", state: "unselected" },
],
},
];
+3 -3
View File
@@ -4,7 +4,7 @@ import {
parseDocumentSectionsForDisplay,
parseSectionsFromCreateFlowState,
} from "../../lib/create/buildPublishPayload";
import type { CreateFlowState } from "../../app/create/types";
import type { CreateFlowState } from "../../app/(app)/create/types";
describe("buildPublishPayload", () => {
it("returns error when title missing", () => {
@@ -97,8 +97,8 @@ describe("buildPublishPayload", () => {
title: "T",
selectedCoreValueIds: ["1", "2"],
coreValuesChipsSnapshot: [
{ id: "1", label: "Alpha", state: "Selected" },
{ id: "2", label: "Beta", state: "Selected" },
{ id: "1", label: "Alpha", state: "selected" },
{ id: "2", label: "Beta", state: "selected" },
],
coreValueDetailsByChipId: {
"1": { meaning: "m1", signals: "s1" },
+1 -1
View File
@@ -3,7 +3,7 @@ import {
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
CREATE_FLOW_MD_UP_GRID_CELL_CLASS,
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
} from "../../app/create/components/createFlowLayoutTokens";
} from "../../app/(app)/create/components/createFlowLayoutTokens";
describe("createFlowLayoutTokens", () => {
it("exports create-flow column and two-column max class strings", () => {
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import { getProportionBarProgressForCreateFlowStep } from "../../app/create/utils/createFlowProportionProgress";
import { getProportionBarProgressForCreateFlowStep } from "../../app/(app)/create/utils/createFlowProportionProgress";
describe("getProportionBarProgressForCreateFlowStep", () => {
it("uses 1-2 on community-structure (third Create Community step)", () => {
+2 -2
View File
@@ -83,8 +83,8 @@ describe("createFlowStateSchema", () => {
const r = createFlowStateSchema.safeParse({
communityStructureChipSnapshots: {
organizationTypes: [
{ id: "1", label: "Co-op", state: "Selected" },
{ id: "custom-uuid", label: "My type", state: "Selected" },
{ id: "1", label: "Co-op", state: "selected" },
{ id: "custom-uuid", label: "My type", state: "selected" },
],
scale: [{ id: "1", label: "Local" }],
maturity: [],
+1 -1
View File
@@ -5,7 +5,7 @@ import {
getPreviousStep,
isValidStep,
getStepIndex,
} from "../../app/create/utils/flowSteps";
} from "../../app/(app)/create/utils/flowSteps";
describe("flowSteps", () => {
it("places confirm-stakeholders immediately before final-review", () => {
+1 -1
View File
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import { hasCreateFlowUserInput } from "../../app/create/utils/hasCreateFlowUserInput";
import { hasCreateFlowUserInput } from "../../app/(app)/create/utils/hasCreateFlowUserInput";
describe("hasCreateFlowUserInput", () => {
it("returns false for empty state", () => {
+1 -1
View File
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { saveDraftToServer } from "../../lib/create/api";
import type { CreateFlowState } from "../../app/create/types";
import type { CreateFlowState } from "../../app/(app)/create/types";
const minimalState: CreateFlowState = {};
+1 -1
View File
@@ -2,7 +2,7 @@ import React, { type ReactElement } from "react";
import { render, type RenderOptions } from "@testing-library/react";
import { AuthModalProvider } from "../../app/contexts/AuthModalContext";
import { MessagesProvider } from "../../app/contexts/MessagesContext";
import { CreateFlowProvider } from "../../app/create/context/CreateFlowContext";
import { CreateFlowProvider } from "../../app/(app)/create/context/CreateFlowContext";
import messages from "../../messages/en/index";
/**