Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions components/shared/FollowButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { BaseFollowButton } from "./FollowButton"

jest.mock("next-i18next", () => ({
useTranslation: () => ({ t: (key: string) => key })
}))

const push = jest.fn()
jest.mock("next/router", () => ({
useRouter: () => ({ push, asPath: "/testimony/abc/1" })
}))

let uid: string | undefined
jest.mock("../auth", () => ({
useAuth: () => ({ user: uid ? { uid } : null })
}))

describe("BaseFollowButton", () => {
beforeEach(() => {
push.mockClear()
uid = undefined
})

it("sends a logged-out visitor to login instead of following", async () => {
const followAction = jest.fn()
render(
<BaseFollowButton
topicName="bill-194-H1"
followAction={followAction}
unfollowAction={jest.fn()}
/>
)

await userEvent.click(screen.getByRole("button"))

expect(followAction).not.toHaveBeenCalled()
expect(push).toHaveBeenCalledWith("/login?redirect=%2Ftestimony%2Fabc%2F1")
})

it("follows when a logged-in user clicks the button", async () => {
uid = "user-1"
const followAction = jest.fn().mockResolvedValue(undefined)
render(
<BaseFollowButton
topicName="bill-194-H1"
followAction={followAction}
unfollowAction={jest.fn()}
/>
)

await userEvent.click(screen.getByRole("button"))

expect(followAction).toHaveBeenCalled()
expect(push).not.toHaveBeenCalled()
})
})
22 changes: 22 additions & 0 deletions components/shared/LabeledIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import { LabeledIcon } from "./LabeledIcon"

describe("LabeledIcon", () => {
it("renders the image with the provided src", () => {
render(<LabeledIcon idImage="/avatar.png" mainText="Main" subText="Sub" />)
expect(screen.getByRole("img")).toHaveAttribute("src", "/avatar.png")
})

it("renders the main and sub text", () => {
render(
<LabeledIcon
idImage="/avatar.png"
mainText="Jane Doe"
subText="Senator"
/>
)
expect(screen.getByText("Jane Doe")).toBeInTheDocument()
expect(screen.getByText("Senator")).toBeInTheDocument()
})
})
21 changes: 21 additions & 0 deletions components/shared/MessageBanner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import { MessageBanner } from "./MessageBanner"

describe("MessageBanner", () => {
it("renders the heading and content", () => {
render(<MessageBanner heading="Welcome" content="Get started here" />)
expect(screen.getByText("Welcome")).toBeInTheDocument()
expect(screen.getByText("Get started here")).toBeInTheDocument()
})

it("renders the icon image when provided", () => {
render(<MessageBanner heading="Welcome" icon="/icon.png" />)
expect(screen.getByRole("img")).toHaveAttribute("src", "/icon.png")
})

it("renders without an icon when none is provided", () => {
render(<MessageBanner heading="Welcome" />)
expect(screen.queryByRole("img")).not.toBeInTheDocument()
})
})
74 changes: 74 additions & 0 deletions components/shared/PaginatedItemsCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { PaginatedItemsCard } from "./PaginatedItemsCard"

jest.mock("next-i18next", () => ({
useTranslation: () => ({ t: (key: string) => key })
}))

const ItemCard = ({ name }: { name: string }) => <div>{name}</div>

const items = Array.from({ length: 25 }, (_, i) => ({ name: `Item ${i + 1}` }))

describe("PaginatedItemsCard", () => {
it("shows only the first page of items", () => {
render(
<PaginatedItemsCard
title="Items"
items={items}
ItemCard={ItemCard}
loading={false}
error={null}
/>
)
expect(screen.getByText("Item 1")).toBeInTheDocument()
expect(screen.getByText("Item 10")).toBeInTheDocument()
expect(screen.queryByText("Item 11")).not.toBeInTheDocument()
})

it("moves to the next page and back", async () => {
const user = userEvent.setup()
render(
<PaginatedItemsCard
title="Items"
items={items}
ItemCard={ItemCard}
loading={false}
error={null}
/>
)
const buttons = screen.getAllByRole("button")
await user.click(buttons[2])
expect(screen.getByText("Item 11")).toBeInTheDocument()
await user.click(buttons[0])
expect(screen.getByText("Item 1")).toBeInTheDocument()
})

it("shows an error alert instead of items when an error is present", () => {
render(
<PaginatedItemsCard
title="Items"
items={items}
ItemCard={ItemCard}
loading={false}
error="Something went wrong"
/>
)
expect(screen.getByText("Something went wrong")).toBeInTheDocument()
expect(screen.queryByText("Item 1")).not.toBeInTheDocument()
})

it("shows a spinner while loading", () => {
const { container } = render(
<PaginatedItemsCard
title="Items"
items={items}
ItemCard={ItemCard}
loading
error={null}
/>
)
expect(container.querySelector(".spinner-border")).toBeInTheDocument()
})
})
29 changes: 29 additions & 0 deletions components/shared/TitledSectionCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import "@testing-library/jest-dom"
import { render, screen } from "@testing-library/react"
import TitledSectionCard from "./TitledSectionCard"

describe("TitledSectionCard", () => {
it("renders the title and children", () => {
render(
<TitledSectionCard title="My Section">
<p>Body content</p>
</TitledSectionCard>
)
expect(screen.getByText("My Section")).toBeInTheDocument()
expect(screen.getByText("Body content")).toBeInTheDocument()
})

it("omits the header when no title is provided", () => {
render(<TitledSectionCard>Body only</TitledSectionCard>)
expect(screen.getByText("Body only")).toBeInTheDocument()
})

it("renders the footer when provided", () => {
render(
<TitledSectionCard title="Section" footer={<button>Save</button>}>
Body
</TitledSectionCard>
)
expect(screen.getByRole("button", { name: "Save" })).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ jest.mock("components/featureFlags", () => ({
})
}))

let uid: string | undefined = "user-1"
jest.mock("components/auth", () => ({
useAuth: () => ({
user: { uid: "user-1" }
user: uid ? { uid } : null
})
}))

const push = jest.fn()
jest.mock("next/router", () => ({
useRouter: () => ({ push, asPath: "/testimony/abc/1" })
}))

jest.mock("components/db/api", () => ({
dbService: () => ({
getBallotQuestion: mockGetBallotQuestion
Expand Down Expand Up @@ -78,6 +84,8 @@ jest.mock("components/publish", () => ({
describe("PolicyActions", () => {
beforeEach(() => {
jest.clearAllMocks()
uid = "user-1"
push.mockClear()
mockFollowsTopic.mockResolvedValue(false)
mockFollowBill.mockResolvedValue(undefined)
mockUnfollowBill.mockResolvedValue(undefined)
Expand Down Expand Up @@ -124,4 +132,33 @@ describe("PolicyActions", () => {
})
expect(mockFollowBill).not.toHaveBeenCalled()
})

it("sends a logged-out visitor to login instead of following", async () => {
uid = undefined

render(
<FollowContext.Provider
value={{ followStatus: {}, setFollowStatus: jest.fn() }}
>
<PolicyActions
isReporting={false}
setReporting={jest.fn()}
isUser={false}
/>
</FollowContext.Provider>
)

fireEvent.click(
screen.getByText("Follow Ballot Question 25-14: Should we do the thing?")
)

await waitFor(() => {
expect(push).toHaveBeenCalledWith(
"/login?redirect=%2Ftestimony%2Fabc%2F1"
)
})

expect(mockFollowBallotQuestion).not.toHaveBeenCalled()
expect(mockFollowBill).not.toHaveBeenCalled()
})
})