Browse Source

Merge pull request #571 from danditomaso/fix/improve-refresh-keys-dialog

fix: improvements to refresh dialog
pull/572/head
Dan Ditomaso 1 year ago
committed by GitHub
parent
commit
b52ed19649
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 107
      src/components/Dialog/RefreshKeysDialog/RefreshKeysDialog.test.tsx
  2. 30
      src/components/Dialog/RefreshKeysDialog/RefreshKeysDialog.tsx
  3. 2
      src/components/UI/Dialog.tsx

107
src/components/Dialog/RefreshKeysDialog/RefreshKeysDialog.test.tsx

@ -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();
});
});
});

30
src/components/Dialog/RefreshKeysDialog/RefreshKeysDialog.tsx

@ -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>

2
src/components/UI/Dialog.tsx

@ -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,

Loading…
Cancel
Save