Drop Cookies Settings from the footer, link the Home features that already have pages, and give leftover routes their own descriptions.
218 lines
7.3 KiB
TypeScript
218 lines
7.3 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
|
|
test.describe("Critical User Journeys", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/");
|
|
});
|
|
|
|
test("complete user journey: learn about CommunityRule", async ({ page }) => {
|
|
// 1. User lands on homepage
|
|
await expect(page.locator("text=Build structure")).toBeVisible();
|
|
|
|
// 2. User reads hero section
|
|
await expect(
|
|
page.locator("text=Strong communities agree on four things early"),
|
|
).toBeVisible();
|
|
|
|
// 3. User clicks CTA to learn more
|
|
const learnCta = page
|
|
.getByRole("link", { name: /Talk to a facilitator/i })
|
|
.or(page.getByRole("button", { name: /Talk to a facilitator/i }))
|
|
.first();
|
|
if ((await learnCta.count()) > 0 && (await learnCta.isVisible())) {
|
|
await learnCta.click();
|
|
await expect(page).toHaveURL(/\/workshops/);
|
|
}
|
|
|
|
// 4. User scrolls to CardSteps section (home)
|
|
// Note: mobile title is one line; lg+ uses stacked "How / CommunityRule / helps" from home copy
|
|
const howItWorksHeading = page.locator(
|
|
'h2:has-text("How CommunityRule helps"), h2:has-text("How"), h2:has-text("CommunityRule"), h2:has-text("helps")',
|
|
);
|
|
await expect(howItWorksHeading).toBeVisible();
|
|
|
|
// 5. User reads the process steps
|
|
await expect(
|
|
page.locator("text=See what has worked for other communities"),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.locator("text=Decide how you will decide, while things are calm"),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.locator(
|
|
"text=Invite your members and change it together as you grow",
|
|
),
|
|
).toBeVisible();
|
|
|
|
// 6. User explores rule templates
|
|
await page.locator("text=Consensus").first().click();
|
|
await page.locator("text=Do-ocracy").first().click();
|
|
await page.locator("text=Devolution").first().click();
|
|
await page.locator("text=Solidarity Network").first().click();
|
|
|
|
// 7. User checks out features
|
|
const features = [
|
|
"Choose how you decide",
|
|
"Name your values",
|
|
"Define membership",
|
|
"Plan for conflict",
|
|
];
|
|
|
|
for (const feature of features) {
|
|
const featureElement = page.locator(`text=${feature}`);
|
|
if (
|
|
(await featureElement.count()) > 0 &&
|
|
(await featureElement.first().isVisible())
|
|
) {
|
|
await featureElement.first().click();
|
|
}
|
|
}
|
|
|
|
// 8. User reads testimonial
|
|
await expect(page.locator("text=Jo Freeman")).toBeVisible();
|
|
|
|
// 9. User decides to contact organizer (opens modal)
|
|
const askButton = page.getByTestId("ask-organizer-cta").first();
|
|
if ((await askButton.count()) > 0 && (await askButton.isVisible())) {
|
|
await askButton.click();
|
|
await expect(
|
|
page.getByRole("dialog", { name: /ask an organizer/i }),
|
|
).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test("homepage loads successfully with all sections", async ({ page }) => {
|
|
// Check page title
|
|
await expect(page).toHaveTitle(/CommunityRule/);
|
|
|
|
// Check main sections are present
|
|
await expect(
|
|
page.locator("h1, h2").filter({ hasText: "Build structure" }),
|
|
).toBeVisible();
|
|
|
|
const howItWorksHeading = page.locator(
|
|
'h2:has-text("How CommunityRule helps"), h2:has-text("How CommunityRule helps")',
|
|
);
|
|
await expect(howItWorksHeading).toBeVisible();
|
|
|
|
await expect(
|
|
page.locator("h2").filter({ hasText: "Tools for the parts that get hard" }),
|
|
).toBeVisible();
|
|
|
|
// Check key components are rendered
|
|
await expect(
|
|
page.getByRole("heading", { name: "CommunityRule Creative Team" }),
|
|
).toBeVisible();
|
|
await expect(page.locator('img[alt="Food Not Bombs"]')).toBeVisible();
|
|
await expect(page.locator("text=Jo Freeman")).toBeVisible();
|
|
});
|
|
|
|
test("feature grid section functionality", async ({ page }) => {
|
|
// Check section header
|
|
await expect(
|
|
page.locator('h2:has-text("Tools for the parts that get hard")'),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.locator(
|
|
"text=Every community runs into the same knots. CommunityRule gives you a way through",
|
|
),
|
|
).toBeVisible();
|
|
|
|
// Check all four feature cards - FeatureGrid uses section with grid layout
|
|
const featureSection = page.locator(
|
|
'section[aria-label="Feature tools and services"], section:has-text("Tools for the parts that get hard")',
|
|
);
|
|
await expect(featureSection.locator("text=Choose how you decide")).toBeVisible();
|
|
await expect(featureSection.locator("text=Name your values")).toBeVisible();
|
|
await expect(featureSection.locator("text=Define membership")).toBeVisible();
|
|
await expect(
|
|
featureSection.locator("text=Plan for conflict"),
|
|
).toBeVisible();
|
|
|
|
await expect(
|
|
featureSection.locator('a[href="#decision-making"]'),
|
|
).toHaveCount(0);
|
|
await expect(
|
|
featureSection.getByRole("link", { name: "Choose how you decide" }),
|
|
).toHaveCount(0);
|
|
await expect(
|
|
featureSection.getByRole("link", { name: "Plan for conflict" }),
|
|
).toHaveCount(0);
|
|
await expect(
|
|
featureSection.getByRole("link", { name: "Name your values" }),
|
|
).toHaveAttribute("href", "/how-it-works");
|
|
await expect(
|
|
featureSection.getByRole("link", { name: "Define membership" }),
|
|
).toHaveAttribute("href", "/blog/integrating-new-members-without-dilution");
|
|
await expect(
|
|
featureSection.getByRole("link", { name: "Explore the patterns" }),
|
|
).toHaveAttribute("href", "/templates");
|
|
});
|
|
|
|
test("header navigation functionality", async ({ page }) => {
|
|
// Check header is present
|
|
await expect(page.locator("header")).toBeVisible();
|
|
|
|
// Test logo click
|
|
const logoLinks = page.locator('a[aria-label="CommunityRule"]');
|
|
const logoCount = await logoLinks.count();
|
|
expect(logoCount).toBeGreaterThan(0);
|
|
|
|
let visibleLogo = null;
|
|
for (let i = 0; i < logoCount; i++) {
|
|
const logo = logoLinks.nth(i);
|
|
if (await logo.isVisible()) {
|
|
visibleLogo = logo;
|
|
break;
|
|
}
|
|
}
|
|
|
|
expect(visibleLogo).not.toBeNull();
|
|
await visibleLogo.click();
|
|
await expect(page).toHaveURL(/\/#?$/);
|
|
});
|
|
|
|
test("keyboard navigation and accessibility", async ({ page }) => {
|
|
// Test tab navigation
|
|
await page.keyboard.press("Tab");
|
|
await expect(page.locator(":focus")).toBeVisible();
|
|
|
|
// Navigate through interactive elements
|
|
await page.keyboard.press("Tab");
|
|
await page.keyboard.press("Tab");
|
|
await page.keyboard.press("Tab");
|
|
|
|
// Test Enter key on buttons
|
|
await page.keyboard.press("Enter");
|
|
|
|
// Basic accessibility checks
|
|
const html = page.locator("html");
|
|
const lang = await html.getAttribute("lang");
|
|
expect(lang).toBeTruthy();
|
|
|
|
// Check for main heading
|
|
const h1 = page.locator("h1").first();
|
|
await expect(h1).toBeVisible();
|
|
});
|
|
|
|
test("responsive design behavior", async ({ page }) => {
|
|
// Test mobile viewport
|
|
await page.setViewportSize({ width: 375, height: 667 });
|
|
await expect(
|
|
page.locator("h1, h2").filter({ hasText: "Build structure" }),
|
|
).toBeVisible();
|
|
|
|
// Test tablet viewport
|
|
await page.setViewportSize({ width: 768, height: 1024 });
|
|
await expect(
|
|
page.locator("h1, h2").filter({ hasText: "Build structure" }),
|
|
).toBeVisible();
|
|
|
|
// Test desktop viewport
|
|
await page.setViewportSize({ width: 1440, height: 900 });
|
|
await expect(
|
|
page.locator("h1, h2").filter({ hasText: "Build structure" }),
|
|
).toBeVisible();
|
|
});
|
|
});
|