| /** The help page. */ |
| |
| import { describe, expect, it, vi } from "vitest"; |
| import { fireEvent, render, screen, within } from "@testing-library/svelte"; |
| |
| import HelpPage from "../components/HelpPage.svelte"; |
| import { anonymousCalendars, makeCalendars } from "./factories"; |
| |
| const noop = () => {}; |
| |
| function props(overrides: Record<string, unknown> = {}) { |
| return { |
| calendars: makeCalendars(), |
| session: { authenticated: true, uid: "alice", logout_url: "/auth?logout=/" }, |
| zone: "local", |
| compareZones: [] as string[], |
| onclose: noop, |
| ...overrides, |
| }; |
| } |
| |
| describe("what it explains", () => { |
| it("covers the three calendars", () => { |
| const { container } = render(HelpPage, { props: props() }); |
| expect(screen.getByText("The three calendars")).toBeInTheDocument(); |
| const terms = [...container.querySelectorAll("dt")].map((term) => term.textContent?.trim()); |
| expect(terms).toEqual(["Personal", "Project", "Foundation"]); |
| expect(container.querySelectorAll("dd")).toHaveLength(3); |
| }); |
| |
| it("has a section for each part of the UI", () => { |
| render(HelpPage, { props: props() }); |
| for (const heading of [ |
| "Who can do what", |
| "Getting around", |
| "Filters and search", |
| "Timezones", |
| "Adding and changing events", |
| "Shortlinks and subscribing", |
| "Signing in", |
| ]) { |
| expect(screen.getByText(heading)).toBeInTheDocument(); |
| } |
| }); |
| |
| it("spells out the audiences in the permissions table", () => { |
| const { container } = render(HelpPage, { props: props() }); |
| const header = container.querySelector("thead")!; |
| for (const column of ["Anyone", "Project member", "Committee member", "Foundation member"]) { |
| expect(within(header).getByText(column)).toBeInTheDocument(); |
| } |
| }); |
| |
| it("lists a row for each capability", () => { |
| const { container } = render(HelpPage, { props: props() }); |
| const rows = container.querySelectorAll("tbody tr"); |
| expect(rows.length).toBeGreaterThanOrEqual(10); |
| expect(screen.getByText("See private foundation events")).toBeInTheDocument(); |
| expect(screen.getByText("Keep personal events")).toBeInTheDocument(); |
| }); |
| |
| it("marks what anonymous visitors cannot do", () => { |
| const { container } = render(HelpPage, { props: props() }); |
| const row = [...container.querySelectorAll("tbody tr")].find((candidate) => |
| candidate.textContent?.startsWith("Add and edit foundation events"), |
| )!; |
| const cells = row.querySelectorAll("td"); |
| expect(within(cells[0] as HTMLElement).getByLabelText("no")).toBeInTheDocument(); |
| expect(within(cells[3] as HTMLElement).getByLabelText("yes")).toBeInTheDocument(); |
| }); |
| }); |
| |
| describe("what it says about you", () => { |
| it("tells an anonymous visitor where they stand", () => { |
| render(HelpPage, { |
| props: props({ calendars: anonymousCalendars, session: { authenticated: false } }), |
| }); |
| expect(screen.getByText(/browsing anonymously/)).toBeInTheDocument(); |
| expect(screen.getByText("Log in")).toHaveAttribute("href", "/auth?login=/"); |
| }); |
| |
| it("puts the mount point in the login link when there is no session yet", () => { |
| const tag = document.createElement("base"); |
| tag.setAttribute("href", "/calendar/"); |
| document.head.appendChild(tag); |
| try { |
| render(HelpPage, { props: props({ calendars: anonymousCalendars, session: null }) }); |
| expect(screen.getByText("Log in")).toHaveAttribute("href", "/calendar/auth?login=/calendar/"); |
| } finally { |
| tag.remove(); |
| } |
| }); |
| |
| it("lists the projects and committees of a signed-in user", () => { |
| render(HelpPage, { props: props() }); |
| const status = screen.getByText(/signed in as alice/); |
| expect(status).toHaveTextContent("httpd, tomcat"); |
| expect(status).toHaveTextContent("httpd committee"); |
| }); |
| |
| it("mentions foundation membership", () => { |
| render(HelpPage, { |
| props: props({ calendars: makeCalendars({ is_member: true }) }), |
| }); |
| expect(screen.getByText(/signed in as alice/)).toHaveTextContent("a foundation member"); |
| }); |
| |
| it("says which timezone setting is in force", () => { |
| const { container } = render(HelpPage, { props: props({ zone: "Asia/Tokyo" }) }); |
| expect(container.querySelector("#timezones")).toHaveTextContent( |
| "It is currently set to Tokyo (UTC+09:00)", |
| ); |
| }); |
| |
| it("explains the comparison timezones", () => { |
| const { container } = render(HelpPage, { props: props() }); |
| expect(container.querySelector("#timezones")).toHaveTextContent( |
| "You can watch more than one clock at once", |
| ); |
| }); |
| |
| it("names the comparison timezones in use", () => { |
| const { container } = render( |
| HelpPage, |
| { props: props({ compareZones: ["Asia/Tokyo", "Europe/Berlin"] }) }, |
| ); |
| expect(container.querySelector("#timezones")).toHaveTextContent( |
| "You are currently comparing against Asia/Tokyo, Europe/Berlin", |
| ); |
| }); |
| }); |
| |
| describe("getting back", () => { |
| it("has a way out at the top and the bottom", async () => { |
| const onclose = vi.fn(); |
| render(HelpPage, { props: props({ onclose }) }); |
| const buttons = screen.getAllByText("Back to the calendar"); |
| expect(buttons).toHaveLength(2); |
| await fireEvent.click(buttons[0]); |
| await fireEvent.click(buttons[1]); |
| expect(onclose).toHaveBeenCalledTimes(2); |
| }); |
| }); |