-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Write unit tests for settings page and components (#290)
- Loading branch information
1 parent
4b29956
commit dc730c3
Showing
9 changed files
with
493 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
src/components/settings/DeleteAccountSetting/DeleteAccountSetting.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { useAuthActions } from "@convex-dev/auth/react"; | ||
import { render, screen, waitFor } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { useMutation } from "convex/react"; | ||
import { toast } from "sonner"; | ||
import { beforeEach, describe, expect, it, vi } from "vitest"; | ||
import { DeleteAccountSetting } from "./DeleteAccountSetting"; | ||
|
||
describe("DeleteAccountSetting", () => { | ||
const mockSignOut = vi.fn(); | ||
const mockDeleteAccount = vi.fn(); | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
(useMutation as unknown as ReturnType<typeof vi.fn>).mockReturnValue( | ||
mockDeleteAccount, | ||
); | ||
(useAuthActions as unknown as ReturnType<typeof vi.fn>).mockReturnValue({ | ||
signOut: mockSignOut, | ||
}); | ||
}); | ||
|
||
it("renders the DeleteAccountSetting component", () => { | ||
render(<DeleteAccountSetting />); | ||
expect( | ||
screen.getByRole("button", { name: "Delete account" }), | ||
).toBeInTheDocument(); | ||
expect( | ||
screen.getByText("Permanently delete your Namesake account and data."), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("opens the delete account modal when the button is clicked", async () => { | ||
const user = userEvent.setup(); | ||
render(<DeleteAccountSetting />); | ||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
expect(screen.getByText("Delete account?")).toBeInTheDocument(); | ||
expect( | ||
screen.getByText( | ||
"This will permanently erase your account and all data.", | ||
), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("shows an error if the confirmation text is incorrect", async () => { | ||
const user = userEvent.setup(); | ||
render(<DeleteAccountSetting />); | ||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
|
||
const input = screen.getByLabelText("Type DELETE to confirm"); | ||
await user.type(input, "WRONG_TEXT"); | ||
|
||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
expect(screen.getByRole("alert")).toHaveTextContent( | ||
"Please type DELETE to confirm.", | ||
); | ||
|
||
expect(mockDeleteAccount).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("submits the form successfully", async () => { | ||
const user = userEvent.setup(); | ||
render(<DeleteAccountSetting />); | ||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
|
||
const input = screen.getByLabelText("Type DELETE to confirm"); | ||
await user.type(input, "DELETE"); | ||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
|
||
await waitFor(() => { | ||
expect(mockDeleteAccount).toHaveBeenCalled(); | ||
expect(mockSignOut).toHaveBeenCalled(); | ||
expect(toast.success).toHaveBeenCalledWith("Account deleted."); | ||
}); | ||
}); | ||
|
||
it("displays an error if account deletion fails", async () => { | ||
const user = userEvent.setup(); | ||
mockDeleteAccount.mockRejectedValue(new Error("Deletion failed")); | ||
|
||
render(<DeleteAccountSetting />); | ||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
|
||
const input = screen.getByLabelText("Type DELETE to confirm"); | ||
await user.type(input, "DELETE"); | ||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
|
||
expect( | ||
await screen.findByText("Failed to delete account. Please try again."), | ||
).toBeInTheDocument(); | ||
expect(toast.success).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("closes the modal when 'Cancel' is clicked", async () => { | ||
const user = userEvent.setup(); | ||
render(<DeleteAccountSetting />); | ||
await user.click(screen.getByRole("button", { name: "Delete account" })); | ||
|
||
await user.click(screen.getByRole("button", { name: "Cancel" })); | ||
await waitFor(() => { | ||
expect(screen.queryByText("Delete account?")).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
}); |
88 changes: 88 additions & 0 deletions
88
src/components/settings/EditBirthplaceSetting/EditBirthplaceSetting.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import type { Doc, Id } from "@convex/_generated/dataModel"; | ||
import { JURISDICTIONS } from "@convex/constants"; | ||
import { render, screen } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { useMutation } from "convex/react"; | ||
import { toast } from "sonner"; | ||
import { beforeEach, describe, expect, it, vi } from "vitest"; | ||
import { EditBirthplaceSetting } from "./EditBirthplaceSetting"; | ||
|
||
describe("EditBirthplaceSetting", () => { | ||
const mockUser: Doc<"users"> = { | ||
_id: "user123" as Id<"users">, | ||
_creationTime: 123, | ||
role: "user", | ||
birthplace: "CA", | ||
}; | ||
const mockSetBirthplace = vi.fn(); | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
(useMutation as unknown as ReturnType<typeof vi.fn>).mockReturnValue( | ||
mockSetBirthplace, | ||
); | ||
}); | ||
|
||
it("renders correct jurisdiction if it exists", () => { | ||
render(<EditBirthplaceSetting user={mockUser} />); | ||
expect(screen.getByText(JURISDICTIONS.CA)).toBeInTheDocument(); | ||
}); | ||
|
||
it("renders 'Set birthplace' if birthplace is not set", () => { | ||
render( | ||
<EditBirthplaceSetting user={{ ...mockUser, birthplace: undefined }} />, | ||
); | ||
expect( | ||
screen.getByRole("button", { name: "Set birthplace" }), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("populates correct jurisdiction when modal is opened", async () => { | ||
const user = userEvent.setup(); | ||
render(<EditBirthplaceSetting user={mockUser} />); | ||
await user.click(screen.getByRole("button", { name: JURISDICTIONS.CA })); | ||
expect( | ||
screen.getByRole("button", { name: `${JURISDICTIONS.CA} State` }), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("updates birthplace and submits the form", async () => { | ||
const user = userEvent.setup(); | ||
mockSetBirthplace.mockResolvedValueOnce(undefined); | ||
|
||
render(<EditBirthplaceSetting user={mockUser} />); | ||
await user.click(screen.getByRole("button", { name: JURISDICTIONS.CA })); | ||
const stateSelect = screen.getByLabelText("State"); | ||
|
||
await user.click(stateSelect); | ||
|
||
await user.click(screen.getByRole("option", { name: JURISDICTIONS.NY })); | ||
|
||
await user.click(screen.getByRole("button", { name: "Save" })); | ||
|
||
expect(mockSetBirthplace).toHaveBeenCalledWith({ | ||
birthplace: "NY", | ||
}); | ||
|
||
expect(toast.success).toHaveBeenCalledWith("Birthplace updated."); | ||
}); | ||
|
||
it("displays an error message if the update fails", async () => { | ||
const user = userEvent.setup(); | ||
mockSetBirthplace.mockRejectedValueOnce(new Error("Update failed")); | ||
|
||
render(<EditBirthplaceSetting user={mockUser} />); | ||
|
||
await user.click(screen.getByRole("button", { name: JURISDICTIONS.CA })); | ||
const stateSelect = screen.getByLabelText("State"); | ||
await user.click(stateSelect); | ||
|
||
await user.click(screen.getByRole("option", { name: JURISDICTIONS.NY })); | ||
|
||
await user.click(screen.getByRole("button", { name: "Save" })); | ||
|
||
expect(screen.getByRole("alert")).toHaveTextContent( | ||
"Failed to update birthplace. Please try again.", | ||
); | ||
}); | ||
}); |
46 changes: 46 additions & 0 deletions
46
src/components/settings/EditMinorSetting/EditMinorSetting.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type { Doc, Id } from "@convex/_generated/dataModel"; | ||
import { render, screen } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { useMutation } from "convex/react"; | ||
import { beforeEach, describe, expect, it, vi } from "vitest"; | ||
import { EditMinorSetting } from "./EditMinorSetting"; | ||
|
||
describe("EditMinorSetting", () => { | ||
const mockUser: Doc<"users"> = { | ||
_id: "user123" as Id<"users">, | ||
isMinor: false, | ||
_creationTime: 123, | ||
role: "user", | ||
}; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
it("renders correctly with initial state", () => { | ||
render(<EditMinorSetting user={mockUser} />); | ||
|
||
expect(screen.getByText("Under 18")).toBeInTheDocument(); | ||
expect( | ||
screen.getByText( | ||
"Are you under 18 years old or applying on behalf of someone who is?", | ||
), | ||
).toBeInTheDocument(); | ||
expect(screen.getByRole("switch", { name: "Is minor" })).not.toBeChecked(); | ||
}); | ||
|
||
it("toggles the switch and calls updateIsMinor mutation", async () => { | ||
const user = userEvent.setup(); | ||
const updateIsMinorMock = vi.fn(); | ||
(useMutation as ReturnType<typeof vi.fn>).mockReturnValue( | ||
updateIsMinorMock, | ||
); | ||
|
||
render(<EditMinorSetting user={mockUser} />); | ||
|
||
const freeSwitch = screen.getByRole("switch", { name: "Is minor" }); | ||
await user.click(freeSwitch); | ||
|
||
expect(updateIsMinorMock).toHaveBeenCalledWith({ isMinor: true }); | ||
}); | ||
}); |
105 changes: 105 additions & 0 deletions
105
src/components/settings/EditNameSetting/EditNameSetting.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import type { Doc, Id } from "@convex/_generated/dataModel"; | ||
import { render, screen, waitFor } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { useMutation } from "convex/react"; | ||
import { toast } from "sonner"; | ||
import { beforeEach, describe, expect, it, vi } from "vitest"; | ||
import { EditNameSetting } from "./EditNameSetting"; | ||
|
||
describe("EditNameSetting", () => { | ||
const mockUser: Doc<"users"> = { | ||
_id: "123" as Id<"users">, | ||
name: "John Doe", | ||
role: "user", | ||
_creationTime: 123, | ||
}; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
it("renders correct username if exists", () => { | ||
render(<EditNameSetting user={mockUser} />); | ||
expect(screen.getByText("John Doe")).toBeInTheDocument(); | ||
}); | ||
|
||
it("renders 'Set name' if name is not set", () => { | ||
render(<EditNameSetting user={{ ...mockUser, name: undefined }} />); | ||
expect( | ||
screen.getByRole("button", { name: "Set name" }), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("populates correct username when modal is opened", async () => { | ||
const user = userEvent.setup(); | ||
render(<EditNameSetting user={mockUser} />); | ||
await user.click(screen.getByRole("button", { name: "John Doe" })); | ||
expect(screen.getByRole("textbox")).toHaveValue("John Doe"); | ||
}); | ||
|
||
it("displays an error when the name is too long", async () => { | ||
const user = userEvent.setup(); | ||
render(<EditNameSetting user={mockUser} />); | ||
await user.click(screen.getByRole("button", { name: "John Doe" })); | ||
const input = screen.getByLabelText("Name"); | ||
|
||
await user.type(input, "a".repeat(101)); | ||
await user.click(screen.getByRole("button", { name: "Save" })); | ||
|
||
expect( | ||
await screen.findByText("Name must be less than 100 characters."), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("submits the form successfully", async () => { | ||
const user = userEvent.setup(); | ||
|
||
const updateName = vi.fn(); | ||
(useMutation as ReturnType<typeof vi.fn>).mockReturnValue(updateName); | ||
render(<EditNameSetting user={mockUser} />); | ||
await user.click(screen.getByRole("button", { name: "John Doe" })); | ||
|
||
const input = screen.getByLabelText("Name"); | ||
await user.clear(input); | ||
await user.type(input, "Jane Doe"); | ||
await user.click(screen.getByRole("button", { name: "Save" })); | ||
|
||
await waitFor(() => | ||
expect(updateName).toHaveBeenCalledWith({ name: "Jane Doe" }), | ||
); | ||
expect(toast.success).toHaveBeenCalledWith("Name updated."); | ||
}); | ||
|
||
it("displays an error when the form submission fails", async () => { | ||
const user = userEvent.setup(); | ||
|
||
const updateName = vi | ||
.fn() | ||
.mockRejectedValue(new Error("Failed to update name")); | ||
(useMutation as ReturnType<typeof vi.fn>).mockReturnValue(updateName); | ||
render(<EditNameSetting user={mockUser} />); | ||
await user.click(screen.getByRole("button", { name: "John Doe" })); | ||
|
||
const input = screen.getByLabelText("Name"); | ||
await user.clear(input); | ||
await user.type(input, "Jane Doe"); | ||
await user.click(screen.getByRole("button", { name: "Save" })); | ||
expect( | ||
await screen.findByText("Failed to update name. Please try again."), | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("closes the modal without saving when the cancel button is clicked", async () => { | ||
const user = userEvent.setup(); | ||
|
||
render(<EditNameSetting user={mockUser} />); | ||
await user.click(screen.getByRole("button", { name: "John Doe" })); | ||
const input = screen.getByLabelText("Name"); | ||
await user.clear(input); | ||
await user.type(input, "Jane Doe"); | ||
await user.click(screen.getByRole("button", { name: "Cancel" })); | ||
|
||
expect(screen.queryByText("Edit name")).not.toBeInTheDocument(); | ||
expect(screen.getByText("John Doe")).toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.