feat: add privacy, terms, and cookies pages

Footer and login legal links were stubs. Point them at real marketing
pages that follow the Figma content-page template.
This commit is contained in:
adilallo
2026-08-17 11:15:04 -06:00
parent 99419915d7
commit 0167b03231
16 changed files with 634 additions and 9 deletions
+8 -5
View File
@@ -110,13 +110,16 @@ describe("Footer (behavioral tests)", () => {
expect(screen.queryByText(/all right reserved/i)).not.toBeInTheDocument();
});
it("renders legal links", () => {
it("renders legal links to privacy, terms, and cookies pages", () => {
render(<Footer />);
expect(
screen.getAllByRole("link", { name: "Privacy Policy" }).length,
).toBeGreaterThan(0);
screen.getAllByRole("link", { name: "Privacy Policy" })[0],
).toHaveAttribute("href", "/privacy");
expect(
screen.getAllByRole("link", { name: "Terms of Service" }).length,
).toBeGreaterThan(0);
screen.getAllByRole("link", { name: "Terms of Service" })[0],
).toHaveAttribute("href", "/terms");
expect(
screen.getAllByRole("link", { name: "Cookies Settings" })[0],
).toHaveAttribute("href", "/cookies");
});
});
+10
View File
@@ -76,6 +76,16 @@ describe("LoginForm", () => {
).toBeInTheDocument();
});
it("links terms and privacy to the legal pages", () => {
renderLoginForm();
expect(
screen.getByRole("link", { name: "Terms of Service" }),
).toHaveAttribute("href", "/terms");
expect(
screen.getByRole("link", { name: "Privacy Policy" }),
).toHaveAttribute("href", "/privacy");
});
it("shows validation error when email is invalid", async () => {
const user = userEvent.setup();
renderLoginForm();
+112
View File
@@ -0,0 +1,112 @@
import { describe, expect, test, vi } from "vitest";
import { screen } from "@testing-library/react";
import { renderWithProviders as render } from "../utils/test-utils";
import PrivacyPage from "../../app/(marketing)/privacy/page";
import TermsPage from "../../app/(marketing)/terms/page";
import CookiesPage from "../../app/(marketing)/cookies/page";
import messages from "../../messages/en/index";
vi.mock("../../app/components/sections/ContentBanner", () => ({
default: ({ post, variant }) => (
<section data-testid="content-banner" data-variant={variant}>
<h1>{post.frontmatter.title}</h1>
<p>{post.frontmatter.description}</p>
</section>
),
}));
describe("legal document pages", () => {
test("privacy page covers hosting, recommendations, and campus privacy", () => {
render(<PrivacyPage />);
const page = messages.pages.privacy;
expect(screen.getByTestId("content-banner")).toHaveAttribute(
"data-variant",
"guide",
);
expect(
screen.getByRole("heading", { level: 1, name: page.banner.title }),
).toBeInTheDocument();
expect(screen.getByText(page.banner.description)).toBeInTheDocument();
expect(screen.getByText(page.updated)).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: "What we collect" }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: "Where it is stored" }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: "Recommendations" }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: "Your account" }),
).toBeInTheDocument();
expect(screen.getByText(/servers MEDLab operates/)).toBeInTheDocument();
expect(screen.getByText(/Amazon SES/)).toBeInTheDocument();
expect(
screen.getByText(/not an AI or machine-learning model/),
).toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Media Economies Design Lab" }),
).toHaveAttribute("href", "https://www.colorado.edu/lab/medlab/");
expect(
screen.getByRole("link", { name: "Privacy Statement" }),
).toHaveAttribute(
"href",
"https://www.colorado.edu/compliance/policies/privacy-statement",
);
});
test("terms page covers licenses and the templates API, not ranking", () => {
render(<TermsPage />);
const page = messages.pages.terms;
expect(
screen.getByRole("heading", { level: 1, name: page.banner.title }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: "Licenses" }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: "Templates API" }),
).toBeInTheDocument();
expect(
screen.queryByRole("heading", { name: "Recommendations" }),
).not.toBeInTheDocument();
expect(
screen.getByRole("link", { name: "CC BY-SA 4.0" }),
).toHaveAttribute(
"href",
"https://creativecommons.org/licenses/by-sa/4.0/",
);
expect(
screen.getByRole("link", { name: "GPL-3.0 or later" }),
).toHaveAttribute("href", "https://www.gnu.org/licenses/gpl-3.0.html");
expect(screen.getByRole("link", { name: "Gitea" })).toHaveAttribute(
"href",
"https://git.medlab.host/CommunityRule/community-rule",
);
expect(
screen.getByRole("link", { name: "GET /api/templates" }),
).toHaveAttribute("href", "/api/templates");
});
test("cookies settings page explains there are no optional cookies", () => {
render(<CookiesPage />);
const page = messages.pages.cookies;
expect(
screen.getByRole("heading", { level: 1, name: page.banner.title }),
).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: "The session cookie" }),
).toBeInTheDocument();
expect(
screen.getByText(/There are no advertising or analytics cookies to turn off/),
).toBeInTheDocument();
expect(screen.getByText(/cr_session/)).toBeInTheDocument();
expect(
screen.getAllByRole("link", { name: "Privacy Policy" })[0],
).toHaveAttribute("href", "/privacy");
});
});
+31
View File
@@ -0,0 +1,31 @@
import { describe, expect, it } from "vitest";
import {
buildLegalSyntheticPost,
legalPathForSlug,
} from "../../lib/legalSyntheticPost";
describe("legalSyntheticPost", () => {
it("maps slugs to public paths", () => {
expect(legalPathForSlug("privacy")).toBe("/privacy");
expect(legalPathForSlug("terms")).toBe("/terms");
expect(legalPathForSlug("cookies")).toBe("/cookies");
});
it("builds a guide-shaped post from page messages", () => {
const post = buildLegalSyntheticPost("privacy", {
banner: {
title: "Privacy Policy",
description: "How we handle information.",
author: "Media Economies Design Lab",
date: "2026-08-15",
},
updated: "Last updated August 15, 2026.",
intro: ["Hello."],
sections: [{ heading: "What we collect", paragraphs: ["Email."] }],
});
expect(post.slug).toBe("__legal__:privacy");
expect(post.frontmatter.title).toBe("Privacy Policy");
expect(post.filePath).toBe("messages/en/pages/privacy.json");
});
});