user-actions.test.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { render, screen } from "@testing-library/react";
  2. import { describe, expect, it, test, vi, afterEach } from "vitest";
  3. import userEvent from "@testing-library/user-event";
  4. import * as Remix from "@remix-run/react";
  5. import { UserActions } from "#/components/user-actions";
  6. describe("UserActions", () => {
  7. const user = userEvent.setup();
  8. const onClickAccountSettingsMock = vi.fn();
  9. const onLogoutMock = vi.fn();
  10. const useFetcherSpy = vi.spyOn(Remix, "useFetcher");
  11. // @ts-expect-error - Only returning the relevant properties for the test
  12. useFetcherSpy.mockReturnValue({ state: "idle" });
  13. afterEach(() => {
  14. onClickAccountSettingsMock.mockClear();
  15. onLogoutMock.mockClear();
  16. useFetcherSpy.mockClear();
  17. });
  18. it("should render", () => {
  19. render(
  20. <UserActions
  21. onClickAccountSettings={onClickAccountSettingsMock}
  22. onLogout={onLogoutMock}
  23. />,
  24. );
  25. expect(screen.getByTestId("user-actions")).toBeInTheDocument();
  26. expect(screen.getByTestId("user-avatar")).toBeInTheDocument();
  27. });
  28. it("should toggle the user menu when the user avatar is clicked", async () => {
  29. render(
  30. <UserActions
  31. onClickAccountSettings={onClickAccountSettingsMock}
  32. onLogout={onLogoutMock}
  33. />,
  34. );
  35. const userAvatar = screen.getByTestId("user-avatar");
  36. await user.click(userAvatar);
  37. expect(
  38. screen.getByTestId("account-settings-context-menu"),
  39. ).toBeInTheDocument();
  40. await user.click(userAvatar);
  41. expect(
  42. screen.queryByTestId("account-settings-context-menu"),
  43. ).not.toBeInTheDocument();
  44. });
  45. it("should call onClickAccountSettings and close the menu when the account settings option is clicked", async () => {
  46. render(
  47. <UserActions
  48. onClickAccountSettings={onClickAccountSettingsMock}
  49. onLogout={onLogoutMock}
  50. />,
  51. );
  52. const userAvatar = screen.getByTestId("user-avatar");
  53. await user.click(userAvatar);
  54. const accountSettingsOption = screen.getByText("Account Settings");
  55. await user.click(accountSettingsOption);
  56. expect(onClickAccountSettingsMock).toHaveBeenCalledOnce();
  57. expect(
  58. screen.queryByTestId("account-settings-context-menu"),
  59. ).not.toBeInTheDocument();
  60. });
  61. it("should call onLogout and close the menu when the logout option is clicked", async () => {
  62. render(
  63. <UserActions
  64. onClickAccountSettings={onClickAccountSettingsMock}
  65. onLogout={onLogoutMock}
  66. user={{ avatar_url: "https://example.com/avatar.png" }}
  67. />,
  68. );
  69. const userAvatar = screen.getByTestId("user-avatar");
  70. await user.click(userAvatar);
  71. const logoutOption = screen.getByText("Logout");
  72. await user.click(logoutOption);
  73. expect(onLogoutMock).toHaveBeenCalledOnce();
  74. expect(
  75. screen.queryByTestId("account-settings-context-menu"),
  76. ).not.toBeInTheDocument();
  77. });
  78. test("onLogout should not be called when the user is not logged in", async () => {
  79. render(
  80. <UserActions
  81. onClickAccountSettings={onClickAccountSettingsMock}
  82. onLogout={onLogoutMock}
  83. />,
  84. );
  85. const userAvatar = screen.getByTestId("user-avatar");
  86. await user.click(userAvatar);
  87. const logoutOption = screen.getByText("Logout");
  88. await user.click(logoutOption);
  89. expect(onLogoutMock).not.toHaveBeenCalled();
  90. });
  91. it("should display the loading spinner", () => {
  92. // @ts-expect-error - Only returning the relevant properties for the test
  93. useFetcherSpy.mockReturnValue({ state: "loading" });
  94. render(
  95. <UserActions
  96. onClickAccountSettings={onClickAccountSettingsMock}
  97. onLogout={onLogoutMock}
  98. user={{ avatar_url: "https://example.com/avatar.png" }}
  99. />,
  100. );
  101. const userAvatar = screen.getByTestId("user-avatar");
  102. user.click(userAvatar);
  103. expect(screen.getByTestId("loading-spinner")).toBeInTheDocument();
  104. expect(screen.queryByAltText("user avatar")).not.toBeInTheDocument();
  105. });
  106. });