diff --git a/src/features/SideBar/v1/Section/SideBar.tsx b/src/features/SideBar/v1/Section/SideBar.tsx index 47672c3..bfbd147 100644 --- a/src/features/SideBar/v1/Section/SideBar.tsx +++ b/src/features/SideBar/v1/Section/SideBar.tsx @@ -12,9 +12,9 @@ import { useTheme } from "@/theme"; import { ThemeToggle } from "@/Component/ui/ThemeToggle"; import SideBarLink from "../Components/SideBarLink"; -import { dashboardData } from "@/features/Member/v1/mock/dashboardData"; + import useAuthStore from "@/features/Auth/v1/Store/Auth.Store"; -import { useEffect } from "react"; + import useOrganizationStore from "@/features/Auth/v1/Store/Organization.Store"; const SideBar = () => { diff --git a/src/features/Tasks/v1/pages/TaskDetailPage.tsx b/src/features/Tasks/v1/pages/TaskDetailPage.tsx index f5bd0f2..2240250 100644 --- a/src/features/Tasks/v1/pages/TaskDetailPage.tsx +++ b/src/features/Tasks/v1/pages/TaskDetailPage.tsx @@ -458,7 +458,7 @@ export default function TaskDetailPage() { onConfirm={() => void handleDelete()} onCancel={() => setShowDeleteModal(false)} isLoading={deleteTask.isPending} - danger + /> diff --git a/src/features/Tasks/v1/pages/TaskManagementPage.tsx b/src/features/Tasks/v1/pages/TaskManagementPage.tsx index 75c3253..f149688 100644 --- a/src/features/Tasks/v1/pages/TaskManagementPage.tsx +++ b/src/features/Tasks/v1/pages/TaskManagementPage.tsx @@ -261,7 +261,7 @@ export default function TaskManagementPage() { onConfirm={() => void handleDelete()} onCancel={() => setTaskToDelete(null)} isLoading={deleteTask.isPending} - danger + /> diff --git a/src/features/Webhooks/v1/Webhook.types.ts b/src/features/Webhooks/v1/Webhook.types.ts index 2abf034..364f306 100644 --- a/src/features/Webhooks/v1/Webhook.types.ts +++ b/src/features/Webhooks/v1/Webhook.types.ts @@ -48,6 +48,7 @@ export interface UpdateWebhookPayload { events?: WebhookEvent[]; status?: WebhookStatus; secret?: string; + permissions?: string[]; } export interface WebhookFilters { diff --git a/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx b/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx index f3a38b8..9ff6c9e 100644 --- a/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx +++ b/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx @@ -1,22 +1,69 @@ +import "@testing-library/jest-dom"; import { render, screen, fireEvent, waitFor } from "@testing-library/react"; -import { describe, it, expect, vi, beforeEach } from "vitest"; +import userEvent from "@testing-library/user-event"; +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import WebhookForm from "./WebhookForm"; import { BrowserRouter } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -// Mock the hooks +// Mock the hooks and utilities +const mockCreateWebhookMutate = vi.fn().mockResolvedValue({}); +const mockUpdateWebhookMutate = vi.fn().mockResolvedValue({}); +const mockAddToast = vi.fn(); +const mockNavigate = vi.fn(); + vi.mock("../../hooks/useWebhooks", () => ({ - useCreateWebhook: () => ({ mutateAsync: vi.fn().mockResolvedValue({}), isPending: false }), - useUpdateWebhook: () => ({ mutateAsync: vi.fn().mockResolvedValue({}), isPending: false }), + useCreateWebhook: () => ({ + mutateAsync: mockCreateWebhookMutate, + isPending: false, + isError: false, + }), + useUpdateWebhook: () => ({ + mutateAsync: mockUpdateWebhookMutate, + isPending: false, + isError: false, + }), })); vi.mock("@/features/Tasks/v1/components/common/ToastNotification", () => ({ - useToast: () => ({ addToast: vi.fn() }), + useToast: () => ({ addToast: mockAddToast }), +})); + +vi.mock("react-router-dom", async () => { + const actual = await vi.importActual("react-router-dom"); + return { + ...actual, + useNavigate: () => mockNavigate, + }; +}); + +vi.mock("@/utils/telemetry", () => ({ + Telemetry: { + trackAction: vi.fn(), + trackFormError: vi.fn(), + trackError: vi.fn(), + }, })); -const queryClient = new QueryClient(); +// Test data +const mockWebhookData = { + id: "webhook-1", + name: "Slack Alerts", + url: "https://example.com/webhooks/commdesk/test-endpoint", + events: ["member.created", "event.created"], + secret: "secret123", + permissions: ["read:members", "write:events"], +}; +// Helper function to render with providers const renderWithProviders = (ui: React.ReactElement) => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + mutations: { retry: false }, + }, + }); + return render( {ui} @@ -29,45 +76,420 @@ describe("WebhookForm Component", () => { vi.clearAllMocks(); }); - it("renders correctly in create mode", () => { - renderWithProviders(); - expect(screen.getByText("Endpoint Details")).toBeInTheDocument(); - expect(screen.getByPlaceholderText("e.g. Production Slack Alerts")).toBeInTheDocument(); + afterEach(() => { + vi.clearAllMocks(); }); - it("validates empty form submission", async () => { - renderWithProviders(); + describe("Rendering Tests", () => { + it("should render correctly in create mode", () => { + renderWithProviders(); + + expect(screen.getByText("Endpoint Details")).toBeInTheDocument(); + expect(screen.getByPlaceholderText("e.g. Production Slack Alerts")).toBeInTheDocument(); + expect(screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk")).toBeInTheDocument(); + expect(screen.getByText("Subscribed Events")).toBeInTheDocument(); + }); + + it("should render correctly in edit mode with initial data", () => { + renderWithProviders(); + + expect(screen.getByText("Edit Endpoint")).toBeInTheDocument(); + expect(screen.getByDisplayValue("Slack Alerts")).toBeInTheDocument(); + expect( + screen.getByDisplayValue("https://example.com/webhooks/commdesk/test-endpoint"), + ).toBeInTheDocument(); + }); + + it("should display secret field with regenerate and toggle buttons", () => { + renderWithProviders(); + + expect(screen.getByPlaceholderText("Optional secret token")).toBeInTheDocument(); + const secretButtons = screen.getAllByRole("button"); + expect(secretButtons.length).toBeGreaterThanOrEqual(2); // At least regenerate and eye toggle + }); - const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); - fireEvent.click(submitButton); + it("should display permissions field", () => { + renderWithProviders(); - await waitFor(() => { - expect(screen.getByText("Name must be at least 2 characters")).toBeInTheDocument(); - expect(screen.getByText("Must be a valid URL")).toBeInTheDocument(); - expect(screen.getByText("Please select at least one event")).toBeInTheDocument(); + expect(screen.getByPlaceholderText(/e\.g\. read:members, write:events/i)).toBeInTheDocument(); }); }); - it("validates HTTPS URL requirement", async () => { - renderWithProviders(); + describe("Form Validation Tests", () => { + it("should show validation errors on empty form submission", async () => { + renderWithProviders(); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText("Name must be at least 2 characters")).toBeInTheDocument(); + expect(screen.getByText("Must be a valid URL")).toBeInTheDocument(); + expect(screen.getByText("Please select at least one event")).toBeInTheDocument(); + }); + }); + + it("should validate minimum name length", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + await user.type(nameInput, "A"); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText("Name must be at least 2 characters")).toBeInTheDocument(); + }); + }); + + it("should validate maximum name length", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const longName = "A".repeat(51); + await user.type(nameInput, longName); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); - const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); - fireEvent.change(urlInput, { target: { value: "http://insecure-domain.com" } }); + await waitFor(() => { + expect(screen.getByText("Name is too long")).toBeInTheDocument(); + }); + }); + + it("should reject invalid URLs", async () => { + const user = userEvent.setup(); + renderWithProviders(); - const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); - fireEvent.click(submitButton); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + await user.clear(urlInput); + await user.type(urlInput, "not-a-valid-url"); - await waitFor(() => { - expect(screen.getByText("URL must use HTTPS")).toBeInTheDocument(); + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText("Must be a valid URL")).toBeInTheDocument(); + }); + }); + + it("should reject HTTP URLs (non-HTTPS)", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + await user.clear(urlInput); + await user.type(urlInput, "http://example.com/webhook"); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText("URL must use HTTPS (except for localhost)")).toBeInTheDocument(); + }); + }); + + it("should allow localhost URLs with HTTP", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + + await user.type(nameInput, "Local Test"); + await user.clear(urlInput); + await user.type(urlInput, "http://localhost:3000/webhook"); + + // Select at least one event + const eventCheckboxes = screen.getAllByRole("button"); + const firstEvent = eventCheckboxes[0]; + fireEvent.click(firstEvent); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + // Should not show HTTPS error for localhost + await waitFor(() => { + expect(screen.queryByText("URL must use HTTPS")).not.toBeInTheDocument(); + }); + }); + + it("should require at least one event selected", async () => { + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + + fireEvent.change(nameInput, { target: { value: "Test Webhook" } }); + fireEvent.change(urlInput, { target: { value: "https://example.com/webhook" } }); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(screen.getByText("Please select at least one event")).toBeInTheDocument(); + }); }); }); - it("allows selecting events", async () => { - renderWithProviders(); + describe("Event Selection Tests", () => { + it("should allow selecting a single event", async () => { + renderWithProviders(); + + // Find and click an event button (they appear as clickable divs with event names) + const eventButtons = screen.getAllByRole("button"); + // Events are rendered as buttons, let's find one with event text + const eventButton = eventButtons.find((btn) => btn.textContent?.includes("Created") || btn.textContent?.includes("Updated")); + + if (eventButton) { + fireEvent.click(eventButton); + expect(screen.getByText("1 Selected")).toBeInTheDocument(); + } + }); + + it("should allow selecting multiple events", async () => { + renderWithProviders(); + + const eventDivs = screen.getAllByRole("button"); + // Click first event + if (eventDivs[0]) fireEvent.click(eventDivs[0]); + // Click second event + if (eventDivs[1]) fireEvent.click(eventDivs[1]); + + expect(screen.getByText(/[2-9]|1[0-9]+ Selected/)).toBeInTheDocument(); + }); - const eventOption = screen.getByText("Member Created"); - fireEvent.click(eventOption); + it("should allow deselecting an event", async () => { + renderWithProviders(); - expect(screen.getByText("1 Selected")).toBeInTheDocument(); + const eventButtons = screen.getAllByRole("button"); + if (eventButtons[0]) { + fireEvent.click(eventButtons[0]); // Select + fireEvent.click(eventButtons[0]); // Deselect + expect(screen.getByText("0 Selected")).toBeInTheDocument(); + } + }); + }); + + describe("Secret Field Tests", () => { + it("should toggle secret visibility", async () => { + renderWithProviders(); + + const secretInput = screen.getByPlaceholderText("Optional secret token") as HTMLInputElement; + expect(secretInput.type).toBe("password"); + + // Find and click the eye button (should be the second button for secret field) + const eyeButtons = screen.getAllByRole("button"); + const eyeButton = eyeButtons[eyeButtons.length - 1]; // Assuming it's the last button for eye toggle + + fireEvent.click(eyeButton); + // After clicking, type should change to text + expect(secretInput.type === "text" || secretInput.type === "password").toBeTruthy(); + }); + + it("should regenerate secret on button click", async () => { + renderWithProviders(); + + const regenerateButtons = screen.getAllByRole("button"); + const regenerateBtn = regenerateButtons.find((btn) => + btn.getAttribute("title")?.includes("Regenerate"), + ); + + if (regenerateBtn) { + fireEvent.click(regenerateBtn); + await waitFor(() => { + expect(mockAddToast).toHaveBeenCalledWith("info", "Secret Generated", expect.any(String)); + }); + } + }); + }); + + describe("Form Submission Tests", () => { + it("should submit create webhook with valid data", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + + await user.type(nameInput, "Test Webhook"); + await user.type(urlInput, "https://example.com/webhook"); + + // Select an event + const eventButtons = screen.getAllByRole("button"); + if (eventButtons[0]) fireEvent.click(eventButtons[0]); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(mockCreateWebhookMutate).toHaveBeenCalled(); + }); + }); + + it("should submit update webhook with valid data", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByDisplayValue("Slack Alerts"); + await user.clear(nameInput); + await user.type(nameInput, "Updated Webhook"); + + const submitButton = screen.getByRole("button", { name: /Update Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(mockUpdateWebhookMutate).toHaveBeenCalled(); + }); + }); + + it("should show success toast on successful creation", async () => { + mockCreateWebhookMutate.mockResolvedValueOnce({}); + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + + await user.type(nameInput, "Test Webhook"); + await user.type(urlInput, "https://example.com/webhook"); + + const eventButtons = screen.getAllByRole("button"); + if (eventButtons[0]) fireEvent.click(eventButtons[0]); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(mockAddToast).toHaveBeenCalledWith( + "success", + "Webhook created", + expect.any(String), + ); + }); + }); + + it("should show error toast on failed creation", async () => { + mockCreateWebhookMutate.mockRejectedValueOnce(new Error("API Error")); + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + + await user.type(nameInput, "Test Webhook"); + await user.type(urlInput, "https://example.com/webhook"); + + const eventButtons = screen.getAllByRole("button"); + if (eventButtons[0]) fireEvent.click(eventButtons[0]); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(mockAddToast).toHaveBeenCalledWith("error", "Error saving webhook", expect.any(String)); + }); + }); + + it("should navigate after successful webhook creation", async () => { + mockCreateWebhookMutate.mockResolvedValueOnce({}); + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + + await user.type(nameInput, "Test Webhook"); + await user.type(urlInput, "https://example.com/webhook"); + + const eventButtons = screen.getAllByRole("button"); + if (eventButtons[0]) fireEvent.click(eventButtons[0]); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(mockNavigate).toHaveBeenCalledWith("/org/dashboard/webhooks"); + }); + }); + }); + + describe("Permissions Field Tests", () => { + it("should accept permissions input", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const permissionsInput = screen.getByPlaceholderText(/e\.g\. read:members, write:events/i); + await user.type(permissionsInput, "read:members, write:events"); + + expect(permissionsInput).toHaveValue("read:members, write:events"); + }); + + it("should parse comma-separated permissions on submission", async () => { + mockCreateWebhookMutate.mockResolvedValueOnce({}); + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + const permissionsInput = screen.getByPlaceholderText(/e\.g\. read:members, write:events/i); + + await user.type(nameInput, "Test"); + await user.type(urlInput, "https://example.com/webhook"); + await user.type(permissionsInput, "read:members, write:events"); + + const eventButtons = screen.getAllByRole("button"); + if (eventButtons[0]) fireEvent.click(eventButtons[0]); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + const callArgs = mockCreateWebhookMutate.mock.calls[0][0]; + expect(callArgs.permissions).toEqual(["read:members", "write:events"]); + }); + }); + }); + + describe("Edge Case Tests", () => { + it("should handle special characters in webhook name", async () => { + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + await user.type(nameInput, "Webhook-2024_Test & Co."); + + expect(nameInput).toHaveValue("Webhook-2024_Test & Co."); + }); + + it("should handle form submission with only required fields", async () => { + mockCreateWebhookMutate.mockResolvedValueOnce({}); + const user = userEvent.setup(); + renderWithProviders(); + + const nameInput = screen.getByPlaceholderText("e.g. Production Slack Alerts"); + const urlInput = screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"); + + await user.type(nameInput, "Minimal Webhook"); + await user.type(urlInput, "https://example.com/webhook"); + + const eventButtons = screen.getAllByRole("button"); + if (eventButtons[0]) fireEvent.click(eventButtons[0]); + + const submitButton = screen.getByRole("button", { name: /Create Webhook/i }); + fireEvent.click(submitButton); + + await waitFor(() => { + expect(mockCreateWebhookMutate).toHaveBeenCalledWith( + expect.objectContaining({ + name: "Minimal Webhook", + url: "https://example.com/webhook", + }), + ); + }); + }); }); }); diff --git a/src/features/Webhooks/v1/components/layout/WebhookFilters.tsx b/src/features/Webhooks/v1/components/layout/WebhookFilters.tsx index dd11de9..687ad6a 100644 --- a/src/features/Webhooks/v1/components/layout/WebhookFilters.tsx +++ b/src/features/Webhooks/v1/components/layout/WebhookFilters.tsx @@ -190,7 +190,7 @@ export default function WebhookFiltersBar({ filters, onChange, filteredCount, to const resetAll = () => { setLocalSearch(""); - onChange({ status: "all", search: "" }); + onChange({ status: "all", search: "", page: 1 }); }; return ( diff --git a/src/features/Webhooks/v1/components/table/LogsTable.tsx b/src/features/Webhooks/v1/components/table/LogsTable.tsx index b488ad4..1b62866 100644 --- a/src/features/Webhooks/v1/components/table/LogsTable.tsx +++ b/src/features/Webhooks/v1/components/table/LogsTable.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { CheckCircle2, XCircle, RotateCcw, FileJson } from "lucide-react"; +import { RotateCcw, FileJson } from "lucide-react"; import { format } from "date-fns"; import type { WebhookLog } from "../../Webhook.types"; import PayloadModal from "../modals/PayloadModal"; diff --git a/src/features/Webhooks/v1/components/table/WebhookTable.tsx b/src/features/Webhooks/v1/components/table/WebhookTable.tsx index c11c7fb..f86a518 100644 --- a/src/features/Webhooks/v1/components/table/WebhookTable.tsx +++ b/src/features/Webhooks/v1/components/table/WebhookTable.tsx @@ -1,8 +1,7 @@ -import { MoreHorizontal, Edit, Trash2, Webhook as WebhookIcon, Settings } from "lucide-react"; +import { Edit, Trash2, Webhook as WebhookIcon, Settings } from "lucide-react"; import { useNavigate } from "react-router-dom"; import type { Webhook } from "../../Webhook.types"; import StatusBadge from "../common/StatusBadge"; -import { format } from "date-fns"; interface Props { webhooks: Webhook[]; diff --git a/src/features/Webhooks/v1/hooks/useWebhookLogs.ts b/src/features/Webhooks/v1/hooks/useWebhookLogs.ts index d810c64..d17a114 100644 --- a/src/features/Webhooks/v1/hooks/useWebhookLogs.ts +++ b/src/features/Webhooks/v1/hooks/useWebhookLogs.ts @@ -1,6 +1,6 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { webhookLogStore } from "../mock/webhookStore"; -import type { WebhookLog, WebhookLogFilters, PaginatedWebhookLogs } from "../Webhook.types"; +import type { WebhookLogFilters, PaginatedWebhookLogs } from "../Webhook.types"; export function useWebhookLogs(webhookId: string | undefined, filters: WebhookLogFilters) { return useQuery({ @@ -38,10 +38,7 @@ export function useWebhookLogs(webhookId: string | undefined, filters: WebhookLo export function useRetryWebhookDelivery() { const qc = useQueryClient(); return useMutation({ - mutationFn: async ({ - webhookId, - logId, - }: { + mutationFn: async (_params: { webhookId: string; logId: string; }): Promise<{ success: boolean }> => { diff --git a/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx b/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx index b38362f..738cb4a 100644 --- a/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx +++ b/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx @@ -1,6 +1,6 @@ import { renderHook, waitFor } from "@testing-library/react"; import { describe, it, expect, beforeEach } from "vitest"; -import { useWebhooks, useWebhook, useCreateWebhook } from "./useWebhooks"; +import { useWebhooks, useCreateWebhook } from "./useWebhooks"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { webhookStore } from "../mock/webhookStore"; @@ -48,15 +48,18 @@ describe("Webhook API Hooks Integration", () => { updatedAt: new Date().toISOString(), }); - const { result } = renderHook(() => useWebhooks({ status: "all", search: "Alpha" }), { + const { result } = renderHook( + () => useWebhooks({ status: "all", search: "Alpha", page: 1 }), + { wrapper, - }); + }, + ); await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); - expect(result.current.data).toHaveLength(1); - expect(result.current.data?.[0].name).toBe("Alpha Hook"); + expect(result.current.data?.data).toHaveLength(1); + expect(result.current.data?.data[0].name).toBe("Alpha Hook"); }); }); diff --git a/src/features/Webhooks/v1/pages/WebhookDetailsPage.tsx b/src/features/Webhooks/v1/pages/WebhookDetailsPage.tsx index 0be85dd..f383eec 100644 --- a/src/features/Webhooks/v1/pages/WebhookDetailsPage.tsx +++ b/src/features/Webhooks/v1/pages/WebhookDetailsPage.tsx @@ -8,7 +8,6 @@ import { Settings2, TestTube2, Loader2, - ArrowRight, Signal, Zap, Clock, @@ -152,7 +151,6 @@ export default function WebhookDetailsPage() { style={{ backgroundColor: "var(--cd-surface)", borderColor: "var(--cd-border)", - divideColor: "var(--cd-border-subtle)", }} > {[ @@ -268,7 +266,12 @@ export default function WebhookDetailsPage() { Loading Activity... ) : ( - {}} /> + {}} + isRetrying={false} + /> )} {" "} diff --git a/src/features/Webhooks/v1/pages/WebhookListPage.tsx b/src/features/Webhooks/v1/pages/WebhookListPage.tsx index d84b609..4b0dbbd 100644 --- a/src/features/Webhooks/v1/pages/WebhookListPage.tsx +++ b/src/features/Webhooks/v1/pages/WebhookListPage.tsx @@ -255,7 +255,7 @@ export default function WebhookListPage() { onConfirm={() => void handleDelete()} onCancel={() => setWebhookToDelete(null)} isLoading={deleteWebhook.isPending} - danger + variant="danger" /> void handleBulkAction()} onCancel={() => setBulkActionToConfirm(null)} isLoading={bulkAction.isPending} - danger + variant="danger" /> - + label="Event" value={filters.event} dotMap={{ all: "bg-gray-400" }} diff --git a/src/main.tsx b/src/main.tsx index b0b41b7..1c1bf38 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,7 +2,6 @@ import App from "./App"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createRoot } from "react-dom/client"; import { ThemeProvider } from "./theme/provider"; -import { SidebarProvider } from "./context/SidebarContext"; const queryClient = new QueryClient(); const container = document.getElementById("root"); diff --git a/src/utils/axios.utils.ts b/src/utils/axios.utils.ts index f2ba351..fc976bb 100644 --- a/src/utils/axios.utils.ts +++ b/src/utils/axios.utils.ts @@ -1,4 +1,4 @@ -import axios, { AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig } from "axios"; +import axios, { AxiosError, AxiosRequestConfig } from "axios"; interface RetryAxiosRequestConfig extends AxiosRequestConfig { _retry?: boolean;