diff --git a/.gitignore b/.gitignore
index 337e8a4..33c81de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,9 @@ npm-cache/
# Per-article body ornaments (Figma Content page Template image fills)
!public/content/blog/*-ornament-*.png
+# Marketing book cover (raster; see docs/guides/static-assets.md)
+!public/assets/marketing/community-rules-cover.png
+
# Visual regression snapshots (allow these)
!tests/e2e/visual-regression.spec.ts-snapshots/
!tests/e2e/visual-regression.spec.ts-snapshots/*.png
diff --git a/app/(marketing)/about/page.tsx b/app/(marketing)/about/page.tsx
index a3695e1..bc3e882 100644
--- a/app/(marketing)/about/page.tsx
+++ b/app/(marketing)/about/page.tsx
@@ -1,5 +1,10 @@
import messages from "../../../messages/en/index";
-import { getAssetPath, governanceBookletPath } from "../../../lib/assetUtils";
+import {
+ ASSETS,
+ getAssetPath,
+ governanceBookletPath,
+ structureBeforeCrisisPath,
+} from "../../../lib/assetUtils";
import { getTranslation } from "../../../lib/i18n/getTranslation";
import AboutHeader from "../../components/type/AboutHeader";
import type { AboutHeaderSegment } from "../../components/type/AboutHeader";
@@ -53,11 +58,20 @@ export default function AboutPage() {
/>
+
-
+
-
-
-
-
-
-
-
-
-
-
diff --git a/public/assets/marketing/governance-booklet.pdf b/public/assets/marketing/governance-booklet.pdf
index ae19c1e..241adce 100644
Binary files a/public/assets/marketing/governance-booklet.pdf and b/public/assets/marketing/governance-booklet.pdf differ
diff --git a/public/assets/marketing/structure-before-crisis.pdf b/public/assets/marketing/structure-before-crisis.pdf
new file mode 100644
index 0000000..ae19c1e
Binary files /dev/null and b/public/assets/marketing/structure-before-crisis.pdf differ
diff --git a/stories/sections/Book.stories.js b/stories/sections/Book.stories.js
index 5d94148..3af7ec3 100644
--- a/stories/sections/Book.stories.js
+++ b/stories/sections/Book.stories.js
@@ -1,5 +1,10 @@
import Book from "../../app/components/sections/Book";
-import { getAssetPath, governanceBookletPath } from "../../lib/assetUtils";
+import {
+ ASSETS,
+ getAssetPath,
+ governanceBookletPath,
+ structureBeforeCrisisPath,
+} from "../../lib/assetUtils";
import messages from "../../messages/en/pages/about.json";
export default {
@@ -11,12 +16,27 @@ export default {
},
};
+const structureBeforeCrisis = messages.structureBeforeCrisis;
+const communityRules = messages.communityRules;
+
export const Default = {
args: {
- title: messages.book.title,
- description: messages.book.description,
- buttonText: messages.book.buttonText,
- buttonHref: getAssetPath(governanceBookletPath()),
- imageAlt: messages.book.imageAlt,
+ title: structureBeforeCrisis.title,
+ description: structureBeforeCrisis.description,
+ buttonText: structureBeforeCrisis.buttonText,
+ buttonHref: getAssetPath(structureBeforeCrisisPath()),
+ imageSrc: getAssetPath(ASSETS.STRUCTURE_BEFORE_CRISIS_COVER),
+ imageAlt: structureBeforeCrisis.imageAlt,
+ },
+};
+
+export const CommunityRules = {
+ args: {
+ title: communityRules.title,
+ description: communityRules.description,
+ buttonText: communityRules.buttonText,
+ buttonHref: getAssetPath(governanceBookletPath()),
+ imageSrc: getAssetPath(ASSETS.COMMUNITY_RULES_COVER),
+ imageAlt: communityRules.imageAlt,
},
};
diff --git a/tests/pages/about.test.tsx b/tests/pages/about.test.tsx
new file mode 100644
index 0000000..02cd578
--- /dev/null
+++ b/tests/pages/about.test.tsx
@@ -0,0 +1,66 @@
+import { afterEach, describe, expect, test } from "vitest";
+import { within } from "@testing-library/react";
+import {
+ cleanup,
+ renderWithProviders as render,
+ screen,
+} from "../utils/test-utils";
+import AboutPage from "../../app/(marketing)/about/page";
+import {
+ ASSETS,
+ getAssetPath,
+ governanceBookletPath,
+ structureBeforeCrisisPath,
+} from "../../lib/assetUtils";
+import messages from "../../messages/en/index";
+
+afterEach(() => {
+ cleanup();
+});
+
+function bookSection(title: string) {
+ const heading = screen.getByRole("heading", { name: title });
+ const section = heading.closest("section");
+ expect(section).not.toBeNull();
+ return within(section as HTMLElement);
+}
+
+describe("AboutPage", () => {
+ test("pairs each book cover, heading, alt, and download with the same title", () => {
+ render();
+ const page = messages.pages.about;
+
+ const structureBeforeCrisis = bookSection(page.structureBeforeCrisis.title);
+ expect(
+ structureBeforeCrisis.getByText(page.structureBeforeCrisis.description),
+ ).toBeInTheDocument();
+ expect(
+ structureBeforeCrisis.getByRole("img", {
+ name: page.structureBeforeCrisis.imageAlt,
+ }),
+ ).toHaveAttribute(
+ "src",
+ getAssetPath(ASSETS.STRUCTURE_BEFORE_CRISIS_COVER),
+ );
+ expect(
+ structureBeforeCrisis.getByRole("link", {
+ name: page.structureBeforeCrisis.buttonText,
+ }),
+ ).toHaveAttribute("href", getAssetPath(structureBeforeCrisisPath()));
+
+ const communityRules = bookSection(page.communityRules.title);
+ expect(
+ communityRules.getByText(page.communityRules.description),
+ ).toBeInTheDocument();
+ expect(
+ communityRules.getByRole("img", {
+ name: page.communityRules.imageAlt,
+ }),
+ ).toHaveAttribute("src", getAssetPath(ASSETS.COMMUNITY_RULES_COVER));
+ expect(
+ communityRules.getByRole("link", {
+ name: page.communityRules.buttonText,
+ }),
+ ).toHaveAttribute("href", getAssetPath(governanceBookletPath()));
+ });
+});
diff --git a/tests/unit/lib/governanceBooklet.test.ts b/tests/unit/lib/governanceBooklet.test.ts
index 185d4cc..319c9ea 100644
--- a/tests/unit/lib/governanceBooklet.test.ts
+++ b/tests/unit/lib/governanceBooklet.test.ts
@@ -1,18 +1,21 @@
-import { existsSync, readFileSync } from "node:fs";
+import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
-import { getAssetPath, governanceBookletPath } from "../../../lib/assetUtils";
+import {
+ governanceBookletPath,
+ structureBeforeCrisisPath,
+} from "../../../lib/assetUtils";
-describe("governance booklet", () => {
- it("ships a PDF at the About Book download path", () => {
- const relative = governanceBookletPath();
- expect(relative).toBe("assets/marketing/governance-booklet.pdf");
- expect(getAssetPath(relative)).toBe("/assets/marketing/governance-booklet.pdf");
+const publicRoot = join(process.cwd(), "public");
- const abs = join(process.cwd(), "public", relative);
- expect(existsSync(abs)).toBe(true);
+function assertPdf(relativePath: string) {
+ const bytes = readFileSync(join(publicRoot, relativePath));
+ expect(bytes.subarray(0, 5).toString("ascii")).toBe("%PDF-");
+}
- const header = readFileSync(abs).subarray(0, 5).toString("latin1");
- expect(header).toBe("%PDF-");
+describe("marketing book PDFs", () => {
+ it("ships both About book downloads as PDFs", () => {
+ assertPdf(governanceBookletPath());
+ assertPdf(structureBeforeCrisisPath());
});
});