From 9791d6c1f817e39adbd511bf321e7d4437f9a439 Mon Sep 17 00:00:00 2001 From: philon- Date: Mon, 12 May 2025 20:00:59 +0200 Subject: [PATCH] Add hook tests --- src/core/hooks/useFavoriteNode.test.ts | 94 +++++++++++++++++++++++++ src/core/hooks/useIgnoreNode.test.ts | 97 ++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 src/core/hooks/useFavoriteNode.test.ts create mode 100644 src/core/hooks/useIgnoreNode.test.ts diff --git a/src/core/hooks/useFavoriteNode.test.ts b/src/core/hooks/useFavoriteNode.test.ts new file mode 100644 index 00000000..6791bfc7 --- /dev/null +++ b/src/core/hooks/useFavoriteNode.test.ts @@ -0,0 +1,94 @@ +import { act, renderHook } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { useFavoriteNode } from "./useFavoriteNode.ts"; +import { Protobuf } from "@meshtastic/core"; + +const mockNode = { + num: 1234, + user: { + longName: "Test Node", + }, + isFavorite: true, +} as unknown | Protobuf.Mesh.NodeInfo; + +const mockUpdateFavorite = vi.fn(); +const mockGetNode = vi.fn(() => mockNode); +const mockToast = vi.fn(); + +vi.mock("@core/stores/deviceStore.ts", () => ({ + useDevice: () => ({ + updateFavorite: mockUpdateFavorite, + getNode: mockGetNode, + }), +})); + +vi.mock("@core/hooks/useToast.ts", () => ({ + useToast: () => ({ + toast: mockToast, + }), +})); + +describe("useFavoriteNode hook", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("calls updateFavorite and shows correct toast", () => { + const { result } = renderHook(() => useFavoriteNode()); + + act(() => { + result.current.updateFavorite({ nodeNum: 1234, isFavorite: true }); + }); + + expect(mockUpdateFavorite).toHaveBeenCalledWith(1234, true); + expect(mockGetNode).toHaveBeenCalledWith(1234); + expect(mockToast).toHaveBeenCalledWith({ + title: "Added Test Node to favorites", + }); + }); + + it("handles removal case", () => { + const { result } = renderHook(() => useFavoriteNode()); + + act(() => { + result.current.updateFavorite({ nodeNum: 1234, isFavorite: false }); + }); + + expect(mockUpdateFavorite).toHaveBeenCalledWith(1234, false); + expect(mockGetNode).toHaveBeenCalledWith(1234); + expect(mockToast).toHaveBeenCalledWith({ + title: "Removed Test Node from favorites", + }); + }); + + it("falls back to 'node' if longName is missing", () => { + mockGetNode.mockReturnValueOnce({ + num: 5678, + user: {}, + }); // no longName + + const { result } = renderHook(() => useFavoriteNode()); + + act(() => { + result.current.updateFavorite({ nodeNum: 5678, isFavorite: true }); + }); + + expect(mockToast).toHaveBeenCalledWith({ + title: "Added node to favorites", + }); + }); + + it("falls back to 'node' if getNode returns undefined", () => { + mockGetNode.mockReturnValueOnce(undefined); + + const { result } = renderHook(() => useFavoriteNode()); + + act(() => { + result.current.updateFavorite({ nodeNum: 9999, isFavorite: false }); + }); + + expect(mockToast).toHaveBeenCalledWith({ + title: "Removed node from favorites", + }); + }); +}); diff --git a/src/core/hooks/useIgnoreNode.test.ts b/src/core/hooks/useIgnoreNode.test.ts new file mode 100644 index 00000000..8ec741f1 --- /dev/null +++ b/src/core/hooks/useIgnoreNode.test.ts @@ -0,0 +1,97 @@ +import { act, renderHook } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { useIgnoreNode } from "./useIgnoreNode.ts"; +import { Protobuf } from "@meshtastic/core"; + +const mockNode = { + num: 1234, + user: { + longName: "Test Node", + }, + isIgnored: true, +} as unknown | Protobuf.Mesh.NodeInfo; + +const mockUpdateIgnore = vi.fn(); +const mockGetNode = vi.fn(() => mockNode); +const mockToast = vi.fn(); + +vi.mock("@core/stores/deviceStore.ts", () => ({ + useDevice: () => ({ + updateIgnored: mockUpdateIgnore, + getNode: mockGetNode, + }), +})); + +vi.mock("@core/hooks/useToast.ts", () => ({ + useToast: () => ({ + toast: mockToast, + }), +})); + +describe("useIgnoreNode hook", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("calls updateIgnored and shows correct toast", () => { + const { result } = renderHook(() => useIgnoreNode()); + + act(() => { + result.current.updateIgnored({ nodeNum: 1234, isIgnored: true }); + }); + + expect(mockUpdateIgnore).toHaveBeenCalledWith(1234, true); + expect(mockGetNode).toHaveBeenCalledWith(1234); + expect(mockToast).toHaveBeenCalledWith({ + title: "Added Test Node to ignore list", + }); + }); + + it("handles removal case", () => { + const { result } = renderHook(() => useIgnoreNode()); + + act(() => { + result.current.updateIgnored({ + nodeNum: 1234, + isIgnored: false, + }); + }); + + expect(mockUpdateIgnore).toHaveBeenCalledWith(1234, false); + expect(mockGetNode).toHaveBeenCalledWith(1234); + expect(mockToast).toHaveBeenCalledWith({ + title: "Removed Test Node from ignore list", + }); + }); + + it("falls back to 'node' if longName is missing", () => { + mockGetNode.mockReturnValueOnce({ + num: 5678, + user: {}, + }); // no longName + + const { result } = renderHook(() => useIgnoreNode()); + + act(() => { + result.current.updateIgnored({ nodeNum: 5678, isIgnored: true }); + }); + + expect(mockToast).toHaveBeenCalledWith({ + title: "Added node to ignore list", + }); + }); + + it("falls back to 'node' if getNode returns undefined", () => { + mockGetNode.mockReturnValueOnce(undefined); + + const { result } = renderHook(() => useIgnoreNode()); + + act(() => { + result.current.updateIgnored({ nodeNum: 9999, isIgnored: false }); + }); + + expect(mockToast).toHaveBeenCalledWith({ + title: "Removed node from ignore list", + }); + }); +});