import { renderWithProviders as render, screen, cleanup, waitFor, } from "../utils/test-utils"; import userEvent from "@testing-library/user-event"; import { vi, describe, test, expect, afterEach } from "vitest"; import React from "react"; import Page from "../../app/page"; // Mock next/dynamic to return components synchronously in tests vi.mock("next/dynamic", () => { return { default: (importFn, options) => { // In tests, resolve the dynamic import immediately and return the component let Component = null; importFn().then((mod) => { Component = mod.default || mod; }); // Return a synchronous wrapper that uses the mocked component return (props) => { // Use the mocked component directly if (Component) { return ; } // Fallback: return the loading placeholder if component not ready return options?.loading ? options.loading() : null; }; }, }; }); import Header from "../../app/components/Header"; import Footer from "../../app/components/Footer"; afterEach(() => { cleanup(); }); describe("User Journey Integration", () => { // TODO: Fix next/dynamic mock to properly handle async component loading test.skip("new user discovers the application through hero section", async () => { const user = userEvent.setup(); render(
, ); // User sees the main value proposition expect( screen.getByText(/Help your community make important decisions/), ).toBeInTheDocument(); // User clicks the main CTA to learn more const learnButtons = screen.getAllByRole("button", { name: "Learn how CommunityRule works", }); const learnButton = learnButtons[0]; await user.click(learnButton); // Wait for dynamically imported NumberedCards component await waitFor(() => { expect(screen.getByText("How CommunityRule works")).toBeInTheDocument(); }); }); // TODO: Fix next/dynamic mock to properly handle async component loading test.skip("user explores different governance types", async () => { const user = userEvent.setup(); render(); // Wait for dynamically imported RuleStack component await waitFor(() => { expect(screen.getByText("Consensus clusters")).toBeInTheDocument(); }); expect(screen.getByText("Elected Board")).toBeInTheDocument(); expect(screen.getByText("Consensus")).toBeInTheDocument(); expect(screen.getByText("Petition")).toBeInTheDocument(); // User clicks on a governance type to create a rule const createButtons = screen.getAllByRole("button", { name: "Create CommunityRule", }); expect(createButtons.length).toBeGreaterThan(0); await user.click(createButtons[0]); // Button should remain interactive expect(createButtons[0]).toBeInTheDocument(); }); test("user navigates through the application using header navigation", async () => { render(
, ); // User clicks on navigation links in header (check that they exist and are clickable) const navigationLinks = screen.getAllByRole("link"); const headerNavLinks = navigationLinks.filter( (link) => link.textContent?.includes("Use Cases") || link.textContent?.includes("Learn") || link.textContent?.includes("About"), ); // Test that navigation links are present and clickable for (const link of headerNavLinks) { expect(link).toBeInTheDocument(); expect(link).toHaveAttribute("href"); } }); test("user seeks help through ask organizer section", async () => { const user = userEvent.setup(); render(); // User scrolls to the bottom and sees the help section expect(screen.getByText("Still have questions?")).toBeInTheDocument(); expect( screen.getByText("Get answers from an experienced organizer"), ).toBeInTheDocument(); // User clicks the ask organizer button (it's actually a link, not a button) const askLink = screen.getByRole("link", { name: /Ask an organizer/i }); await user.click(askLink); expect(askLink).toHaveAttribute("href", "#contact"); }); test("user explores the process through numbered cards", async () => { render(); // Wait for dynamically imported NumberedCards component await waitFor(() => { expect( screen.getByText("Document how your community makes decisions"), ).toBeInTheDocument(); }); expect( screen.getByText("Build an operating manual for a successful community"), ).toBeInTheDocument(); expect( screen.getByText( "Get a link to your manual for your group to review and evolve", ), ).toBeInTheDocument(); // User sees the step numbers const stepNumbers = screen.getAllByText(/1|2|3/); expect(stepNumbers.length).toBeGreaterThan(0); }); test("user accesses contact information through footer", async () => { render(
, ); // User finds contact email in footer const emailLink = screen.getByRole("link", { name: "medlab@colorado.edu" }); expect(emailLink).toHaveAttribute("href", "mailto:medlab@colorado.edu"); // User finds social media links const blueskyLink = screen.getByRole("link", { name: "Follow us on Bluesky", }); const gitlabLink = screen.getByRole("link", { name: "Follow us on GitLab", }); expect(blueskyLink).toBeInTheDocument(); expect(gitlabLink).toBeInTheDocument(); }); test("user explores features and benefits", async () => { render(); // Wait for dynamically imported FeatureGrid component await waitFor(() => { expect( screen.getByText("We've got your back, every step of the way"), ).toBeInTheDocument(); }); expect( screen.getByText( "Use our toolkit to improve, document, and evolve your organization.", ), ).toBeInTheDocument(); // User sees the testimonial/quote (check for the actual quote content) expect( screen.getByText(/The rules of decision-making must be open/), ).toBeInTheDocument(); }); test("user interacts with logo wall and social proof", async () => { render(
, ); // Wait for dynamically imported LogoWall component await waitFor(() => { const logoImages = screen.getAllByRole("img"); 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"), ); expect(partnerLogos.length).toBeGreaterThan(0); }); // Social links should be present in footer const blueskyLink = screen.getByRole("link", { name: /Bluesky/i }); const gitlabLink = screen.getByRole("link", { name: /GitLab/i }); expect(blueskyLink).toBeInTheDocument(); expect(gitlabLink).toBeInTheDocument(); }); test("user completes the full journey from discovery to action", async () => { render(
, ); // 1. User discovers the application expect(screen.getByText("Collaborate")).toBeInTheDocument(); expect(screen.getByText("with clarity")).toBeInTheDocument(); // 2. User learns how it works - wait for dynamically imported component await waitFor(() => { expect(screen.getByText("How CommunityRule works")).toBeInTheDocument(); }); // 3. User sees governance options - wait for dynamically imported component await waitFor(() => { expect(screen.getByText("Consensus clusters")).toBeInTheDocument(); }); // 4. User sees features and benefits - wait for dynamically imported component await waitFor(() => { expect( screen.getByText("We've got your back, every step of the way"), ).toBeInTheDocument(); }); // 5. User sees social proof expect( screen.getByText(/The rules of decision-making must be open/), ).toBeInTheDocument(); // 6. User can take action const createButtons = screen.getAllByRole("button", { name: "Create CommunityRule", }); expect(createButtons.length).toBeGreaterThan(0); // 7. User can get help if needed expect(screen.getByText("Still have questions?")).toBeInTheDocument(); }); test("user can access all navigation options consistently", async () => { render(
, ); // Header navigation const headerNav = screen.getByRole("navigation"); expect(headerNav).toBeInTheDocument(); // Footer navigation const footerLinks = screen.getAllByRole("link"); const navigationLinks = footerLinks.filter( (link) => link.textContent?.includes("Use cases") || link.textContent?.includes("Learn") || link.textContent?.includes("About"), ); expect(navigationLinks.length).toBeGreaterThan(0); // All navigation links should be accessible navigationLinks.forEach((link) => { expect(link).not.toHaveAttribute("tabindex", "-1"); }); }); test("user can complete actions without errors", async () => { const user = userEvent.setup(); render(
, ); // Test all interactive elements const buttons = screen.getAllByRole("button"); const links = screen.getAllByRole("link"); // All buttons should be clickable for (const button of buttons) { await user.click(button); expect(button).toBeInTheDocument(); } // All links should be accessible for (const link of links) { expect(link).toBeInTheDocument(); expect(link).not.toHaveAttribute("tabindex", "-1"); } }); });