fix: improvements to refresh dialog
This commit is contained in:
@@ -1,55 +1,102 @@
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, Mock, vi } from "vitest";
|
||||
import { RefreshKeysDialog } from "./RefreshKeysDialog";
|
||||
import { beforeEach, describe, expect, it, vi, Mock } from "vitest";
|
||||
import { RefreshKeysDialog } from "./RefreshKeysDialog.tsx";
|
||||
import { useRefreshKeysDialog } from "./useRefreshKeysDialog.ts";
|
||||
import { useMessageStore } from "@core/stores/messageStore.ts"; // Import for mocking
|
||||
import { useDevice } from "@core/stores/deviceStore.ts"; // Import for mocking
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
|
||||
|
||||
vi.mock("@core/stores/messageStore.ts", () => ({
|
||||
useMessageStore: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockNodeWithError: Partial<Protobuf.Mesh.NodeInfo> = {
|
||||
user: { longName: "Test Node Long", shortName: "TNL", id: 456 },
|
||||
};
|
||||
const mockNodes = new Map([[456, mockNodeWithError]]);
|
||||
const mockNodeErrors = new Map([[123, { node: 456 }]]);
|
||||
|
||||
vi.mock("@core/stores/deviceStore.ts", () => ({
|
||||
useDevice: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockHandleCloseDialog = vi.fn();
|
||||
const mockHandleNodeRemove = vi.fn();
|
||||
vi.mock("./useRefreshKeysDialog.ts", () => ({
|
||||
useRefreshKeysDialog: vi.fn(),
|
||||
useRefreshKeysDialog: vi.fn(() => ({
|
||||
handleCloseDialog: mockHandleCloseDialog,
|
||||
handleNodeRemove: mockHandleNodeRemove,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe("RefreshKeysDialog Component", () => {
|
||||
let handleCloseDialogMock: Mock;
|
||||
let handleNodeRemoveMock: Mock;
|
||||
let onOpenChangeMock: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
handleCloseDialogMock = vi.fn();
|
||||
handleNodeRemoveMock = vi.fn();
|
||||
vi.clearAllMocks();
|
||||
onOpenChangeMock = vi.fn();
|
||||
|
||||
(useRefreshKeysDialog as Mock).mockReturnValue({
|
||||
handleCloseDialog: handleCloseDialogMock,
|
||||
handleNodeRemove: handleNodeRemoveMock,
|
||||
vi.mocked(useMessageStore).mockReturnValue({ activeChat: 123 });
|
||||
vi.mocked(useDevice).mockReturnValue({
|
||||
nodeErrors: mockNodeErrors,
|
||||
nodes: mockNodes,
|
||||
});
|
||||
vi.mocked(useRefreshKeysDialog).mockReturnValue({
|
||||
handleCloseDialog: mockHandleCloseDialog,
|
||||
handleNodeRemove: mockHandleNodeRemove,
|
||||
});
|
||||
});
|
||||
|
||||
it("renders the dialog with correct content", () => {
|
||||
render(<RefreshKeysDialog open={true} onOpenChange={onOpenChangeMock} />);
|
||||
expect(screen.getByText("Keys Mismatch")).toBeInTheDocument();
|
||||
expect(screen.getByText("Request New Keys")).toBeInTheDocument();
|
||||
expect(screen.getByText("Dismiss")).toBeInTheDocument();
|
||||
it("should render the dialog with dynamic content when open and data is available", () => {
|
||||
render(<RefreshKeysDialog open onOpenChange={onOpenChangeMock} />);
|
||||
|
||||
expect(screen.getByText(`Keys Mismatch - ${mockNodeWithError?.user?.longName}`)).toBeInTheDocument();
|
||||
expect(screen.getByText(new RegExp(`${mockNodeWithError?.user?.longName}.*${mockNodeWithError?.user?.shortName}`))).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /request new keys/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /dismiss/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /Close/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls handleNodeRemove when 'Request New Keys' button is clicked", () => {
|
||||
render(<RefreshKeysDialog open={true} onOpenChange={onOpenChangeMock} />);
|
||||
fireEvent.click(screen.getByText("Request New Keys"));
|
||||
expect(handleNodeRemoveMock).toHaveBeenCalled();
|
||||
it("should call handleNodeRemove when 'Request New Keys' button is clicked", () => {
|
||||
render(<RefreshKeysDialog open onOpenChange={onOpenChangeMock} />);
|
||||
fireEvent.click(screen.getByRole('button', { name: /request new keys/i }));
|
||||
expect(mockHandleNodeRemove).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("calls handleCloseDialog when 'Dismiss' button is clicked", () => {
|
||||
render(<RefreshKeysDialog open={true} onOpenChange={onOpenChangeMock} />);
|
||||
fireEvent.click(screen.getByText("Dismiss"));
|
||||
expect(handleCloseDialogMock).toHaveBeenCalled();
|
||||
it("should call handleCloseDialog when 'Dismiss' button is clicked", () => {
|
||||
render(<RefreshKeysDialog open onOpenChange={onOpenChangeMock} />);
|
||||
fireEvent.click(screen.getByRole('button', { name: /dismiss/i }));
|
||||
expect(mockHandleCloseDialog).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("calls onOpenChange when dialog close button is clicked", () => {
|
||||
render(<RefreshKeysDialog open={true} onOpenChange={onOpenChangeMock} />);
|
||||
fireEvent.click(screen.getByRole("button", { name: /close/i }));
|
||||
expect(handleCloseDialogMock).toHaveBeenCalled();
|
||||
it("should call handleCloseDialog when the explicit DialogClose button is clicked", () => {
|
||||
render(<RefreshKeysDialog open onOpenChange={onOpenChangeMock} />);
|
||||
fireEvent.click(screen.getByRole('button', { name: /close/i })); // Use the aria-label
|
||||
expect(mockHandleCloseDialog).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not render when open is false", () => {
|
||||
|
||||
it("should not render the dialog when open is false", () => {
|
||||
render(<RefreshKeysDialog open={false} onOpenChange={onOpenChangeMock} />);
|
||||
expect(screen.queryByText("Keys Mismatch")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should render null if nodeErrorNum is not found for activeChat", () => {
|
||||
vi.mocked(useDevice).mockReturnValue({
|
||||
nodeErrors: new Map(),
|
||||
nodes: mockNodes,
|
||||
});
|
||||
const { container } = render(<RefreshKeysDialog open onOpenChange={onOpenChangeMock} />);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it("should render null if nodeWithError is not found for nodeErrorNum.node", () => {
|
||||
vi.mocked(useDevice).mockReturnValue({
|
||||
nodeErrors: mockNodeErrors,
|
||||
nodes: new Map(),
|
||||
});
|
||||
const { container } = render(<RefreshKeysDialog open onOpenChange={onOpenChangeMock} />);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
import { Button } from "@components/UI/Button.tsx";
|
||||
import { LockKeyholeOpenIcon } from "lucide-react";
|
||||
import { useRefreshKeysDialog } from "./useRefreshKeysDialog.ts";
|
||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||
import { useMessageStore } from "@core/stores/messageStore.ts";
|
||||
|
||||
export interface RefreshKeysDialogProps {
|
||||
open: boolean;
|
||||
@@ -15,16 +17,36 @@ export interface RefreshKeysDialogProps {
|
||||
}
|
||||
|
||||
export const RefreshKeysDialog = ({ open, onOpenChange }: RefreshKeysDialogProps) => {
|
||||
|
||||
const { activeChat } = useMessageStore();
|
||||
const { nodeErrors, nodes } = useDevice();
|
||||
const { handleCloseDialog, handleNodeRemove } = useRefreshKeysDialog();
|
||||
|
||||
const nodeErrorNum = nodeErrors.get(activeChat);
|
||||
|
||||
if (!nodeErrorNum) {
|
||||
console.error("Node with error not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
const nodeWithError = nodes.get(nodeErrorNum?.node ?? 0);
|
||||
|
||||
if (!nodeWithError) {
|
||||
console.error("Node with error not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
const text = {
|
||||
title: `Keys Mismatch - ${nodeWithError?.user?.longName ?? ""}`,
|
||||
description: `Your node is unable to send a direct message to node: ${nodeWithError?.user?.longName ?? ""} (${nodeWithError?.user?.shortName ?? ""}). This is due to the remote node's current public key does not match the previously stored key for this node.`,
|
||||
}
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-8 flex flex-col gap-2">
|
||||
<DialogClose onClick={handleCloseDialog} />
|
||||
<DialogHeader>
|
||||
<DialogTitle>Keys Mismatch</DialogTitle>
|
||||
<DialogTitle>{text.title}</DialogTitle>
|
||||
</DialogHeader>
|
||||
Your node is unable to send a direct message to this node. This is due to the remote node's current public key not matching the previously stored key for this node.
|
||||
{text.description}
|
||||
<ul className="mt-2">
|
||||
<li className="flex place-items-center gap-2 items-start">
|
||||
<div className="p-2 bg-slate-500 rounded-lg mt-1">
|
||||
@@ -40,14 +62,12 @@ export const RefreshKeysDialog = ({ open, onOpenChange }: RefreshKeysDialogProps
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={handleNodeRemove}
|
||||
className=""
|
||||
>
|
||||
Request New Keys
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleCloseDialog}
|
||||
className=""
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
|
||||
@@ -60,7 +60,7 @@ const DialogClose = ({
|
||||
...props
|
||||
}: DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement> & { className?: string }) => (
|
||||
<DialogPrimitive.Close
|
||||
name="close"
|
||||
aria-label="Close"
|
||||
className={cn(
|
||||
"absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900",
|
||||
className,
|
||||
|
||||
Reference in New Issue
Block a user