From f2166c985fa492f92a072fb22b5c4caf22ad651e Mon Sep 17 00:00:00 2001 From: Alex Andru Date: Fri, 4 Jul 2025 10:59:29 +0200 Subject: [PATCH] refactor: remove dead code --- client/src/components/ElicitationModal.tsx | 181 ------ .../__tests__/ElicitationModal.test.tsx | 535 ------------------ 2 files changed, 716 deletions(-) delete mode 100644 client/src/components/ElicitationModal.tsx delete mode 100644 client/src/components/__tests__/ElicitationModal.test.tsx diff --git a/client/src/components/ElicitationModal.tsx b/client/src/components/ElicitationModal.tsx deleted file mode 100644 index 8a742a31..00000000 --- a/client/src/components/ElicitationModal.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { useState, useEffect } from "react"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import DynamicJsonForm from "./DynamicJsonForm"; -import { JsonSchemaType, JsonValue } from "@/utils/jsonUtils"; -import { generateDefaultValue } from "@/utils/schemaUtils"; -import Ajv from "ajv"; - -export interface ElicitationRequest { - id: number; - message: string; - requestedSchema: JsonSchemaType; - resolve: (response: ElicitationResponse) => void; -} - -export interface ElicitationResponse { - action: "accept" | "reject" | "cancel"; - content?: Record; -} - -interface ElicitationModalProps { - request: ElicitationRequest | null; - onClose: () => void; -} - -const ElicitationModal = ({ request, onClose }: ElicitationModalProps) => { - const [formData, setFormData] = useState({}); - const [validationError, setValidationError] = useState(null); - - useEffect(() => { - if (request) { - const defaultValue = generateDefaultValue(request.requestedSchema); - setFormData(defaultValue); - setValidationError(null); - } - }, [request]); - - if (!request) return null; - - const validateEmailFormat = (email: string): boolean => { - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - return emailRegex.test(email); - }; - - const validateFormData = ( - data: JsonValue, - schema: JsonSchemaType, - ): boolean => { - if ( - schema.type === "object" && - schema.properties && - typeof data === "object" && - data !== null - ) { - const dataObj = data as Record; - - if (Array.isArray(schema.required)) { - for (const field of schema.required) { - const value = dataObj[field]; - if (value === undefined || value === null || value === "") { - setValidationError(`Required field missing: ${field}`); - return false; - } - } - } - - for (const [fieldName, fieldValue] of Object.entries(dataObj)) { - const fieldSchema = schema.properties[fieldName]; - if ( - fieldSchema && - fieldSchema.format === "email" && - typeof fieldValue === "string" - ) { - if (!validateEmailFormat(fieldValue)) { - setValidationError(`Invalid email format: ${fieldName}`); - return false; - } - } - } - } - - return true; - }; - - const handleAccept = () => { - try { - if (!validateFormData(formData, request.requestedSchema)) { - return; - } - - const ajv = new Ajv(); - const validate = ajv.compile(request.requestedSchema); - const isValid = validate(formData); - - if (!isValid) { - const errorMessage = ajv.errorsText(validate.errors); - setValidationError(errorMessage); - return; - } - - request.resolve({ - action: "accept", - content: formData as Record, - }); - onClose(); - } catch (error) { - setValidationError( - error instanceof Error ? error.message : "Validation failed", - ); - } - }; - - const handleReject = () => { - request.resolve({ action: "reject" }); - onClose(); - }; - - const handleCancel = () => { - request.resolve({ action: "cancel" }); - onClose(); - }; - - const schemaTitle = request.requestedSchema.title || "Information Request"; - const schemaDescription = request.requestedSchema.description; - - return ( - - - - {schemaTitle} - - {request.message} - {schemaDescription && ( - - {schemaDescription} - - )} - - - -
- { - setFormData(newValue); - setValidationError(null); - }} - /> - - {validationError && ( -
-
- Validation Error: {validationError} -
-
- )} -
- - - - - - -
-
- ); -}; - -export default ElicitationModal; diff --git a/client/src/components/__tests__/ElicitationModal.test.tsx b/client/src/components/__tests__/ElicitationModal.test.tsx deleted file mode 100644 index 31a06815..00000000 --- a/client/src/components/__tests__/ElicitationModal.test.tsx +++ /dev/null @@ -1,535 +0,0 @@ -import { render, screen, fireEvent, act } from "@testing-library/react"; -import "@testing-library/jest-dom"; -import { describe, it, jest, beforeEach, afterEach } from "@jest/globals"; -import ElicitationModal, { ElicitationRequest } from "../ElicitationModal"; -import { JsonSchemaType } from "@/utils/jsonUtils"; -import * as schemaUtils from "@/utils/schemaUtils"; - -jest.mock("../DynamicJsonForm", () => { - return function MockDynamicJsonForm({ - value, - onChange, - }: { - value: unknown; - onChange: (value: unknown) => void; - }) { - return ( -
- { - try { - const parsed = JSON.parse(e.target.value); - onChange(parsed); - } catch { - onChange(e.target.value); - } - }} - /> -
- ); - }; -}); - -jest.mock("@/components/ui/dialog", () => ({ - Dialog: ({ - children, - open, - onOpenChange, - }: { - children: React.ReactNode; - open: boolean; - onOpenChange: () => void; - }) => - open ? ( -
- {children} -
- ) : null, - DialogContent: ({ - children, - className, - }: { - children: React.ReactNode; - className?: string; - }) => ( -
- {children} -
- ), - DialogHeader: ({ children }: { children: React.ReactNode }) => ( -
{children}
- ), - DialogTitle: ({ children }: { children: React.ReactNode }) => ( -

{children}

- ), - DialogDescription: ({ children }: { children: React.ReactNode }) => ( -
{children}
- ), - DialogFooter: ({ children }: { children: React.ReactNode }) => ( -
{children}
- ), -})); - -jest.mock("@/components/ui/button", () => ({ - Button: ({ - children, - onClick, - variant, - ...props - }: { - children: React.ReactNode; - onClick?: () => void; - variant?: string; - [key: string]: unknown; - }) => ( - - ), -})); - -jest.mock("@/utils/schemaUtils", () => ({ - generateDefaultValue: jest.fn((schema: JsonSchemaType) => { - if (schema.type === "object" && schema.properties) { - const defaults: Record = {}; - Object.keys(schema.properties).forEach((key) => { - const prop = schema.properties![key]; - if (prop.type === "string") defaults[key] = ""; - if (prop.type === "number") defaults[key] = 0; - if (prop.type === "boolean") defaults[key] = false; - }); - return defaults; - } - return {}; - }), -})); - -describe("ElicitationModal", () => { - const mockResolve = jest.fn(); - const mockOnClose = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - const createMockRequest = ( - overrides: Partial = {}, - ): ElicitationRequest => ({ - id: 1, - message: "Please provide your information", - requestedSchema: { - type: "object", - properties: { - name: { type: "string", description: "Your name" }, - email: { type: "string", format: "email", description: "Your email" }, - }, - required: ["name"], - }, - resolve: mockResolve, - ...overrides, - }); - - const renderElicitationModal = ( - request: ElicitationRequest | null = null, - ) => { - return render( - , - ); - }; - - describe("Rendering", () => { - it("should render null when no request is provided", () => { - render(); - expect(screen.queryByTestId("dialog")).not.toBeInTheDocument(); - }); - - it("should render dialog when request is provided", () => { - renderElicitationModal(); - expect(screen.getByTestId("dialog")).toBeInTheDocument(); - expect(screen.getByTestId("dialog-content")).toBeInTheDocument(); - }); - - it("should display request message", () => { - const message = "Please provide your GitHub username"; - renderElicitationModal(createMockRequest({ message })); - expect(screen.getByText(message)).toBeInTheDocument(); - }); - - it("should display schema title when provided", () => { - const request = createMockRequest({ - requestedSchema: { - type: "object", - title: "User Information", - properties: { name: { type: "string" } }, - }, - }); - renderElicitationModal(request); - expect(screen.getByText("User Information")).toBeInTheDocument(); - }); - - it("should display default title when schema title is not provided", () => { - renderElicitationModal(); - expect(screen.getByText("Information Request")).toBeInTheDocument(); - }); - - it("should display schema description when provided", () => { - const description = "Please fill out your contact details"; - const request = createMockRequest({ - requestedSchema: { - type: "object", - description, - properties: { name: { type: "string" } }, - }, - }); - renderElicitationModal(request); - expect(screen.getByText(description)).toBeInTheDocument(); - }); - - it("should render all three action buttons", () => { - renderElicitationModal(); - expect(screen.getByTestId("button-cancel")).toBeInTheDocument(); - expect(screen.getByTestId("button-decline")).toBeInTheDocument(); - expect(screen.getByTestId("button-submit")).toBeInTheDocument(); - }); - - it("should render DynamicJsonForm component", () => { - renderElicitationModal(); - expect(screen.getByTestId("dynamic-json-form")).toBeInTheDocument(); - }); - }); - - describe("User Interactions", () => { - it("should call resolve with accept action when Submit button is clicked", async () => { - renderElicitationModal(); - - const input = screen.getByTestId("form-input"); - await act(async () => { - fireEvent.change(input, { - target: { - value: '{"name": "John Doe", "email": "john@example.com"}', - }, - }); - }); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect(mockResolve).toHaveBeenCalledWith({ - action: "accept", - content: { name: "John Doe", email: "john@example.com" }, - }); - expect(mockOnClose).toHaveBeenCalled(); - }); - - it("should call resolve with reject action when Decline button is clicked", async () => { - renderElicitationModal(); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-decline")); - }); - - expect(mockResolve).toHaveBeenCalledWith({ action: "reject" }); - expect(mockOnClose).toHaveBeenCalled(); - }); - - it("should call resolve with cancel action when Cancel button is clicked", async () => { - renderElicitationModal(); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-cancel")); - }); - - expect(mockResolve).toHaveBeenCalledWith({ action: "cancel" }); - expect(mockOnClose).toHaveBeenCalled(); - }); - - it("should call resolve with cancel action when dialog is closed", async () => { - renderElicitationModal(); - - await act(async () => { - fireEvent.click(screen.getByTestId("dialog")); - }); - - expect(mockResolve).toHaveBeenCalledWith({ action: "cancel" }); - expect(mockOnClose).toHaveBeenCalled(); - }); - }); - - describe("Form Validation", () => { - it("should show validation error for missing required fields", async () => { - renderElicitationModal(); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect( - screen.getByText(/Required field missing: name/), - ).toBeInTheDocument(); - expect(mockResolve).not.toHaveBeenCalledWith( - expect.objectContaining({ action: "accept" }), - ); - }); - - it("should show validation error for invalid email format", async () => { - renderElicitationModal(); - - const input = screen.getByTestId("form-input"); - await act(async () => { - fireEvent.change(input, { - target: { value: '{"name": "John", "email": "invalid-email"}' }, - }); - }); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect( - screen.getByText(/Invalid email format: email/), - ).toBeInTheDocument(); - expect(mockResolve).not.toHaveBeenCalledWith( - expect.objectContaining({ action: "accept" }), - ); - }); - - it("should clear validation error when form data changes", async () => { - renderElicitationModal(); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect(screen.getByText(/Required field missing/)).toBeInTheDocument(); - - const input = screen.getByTestId("form-input"); - await act(async () => { - fireEvent.change(input, { target: { value: '{"name": "John"}' } }); - }); - - expect( - screen.queryByText(/Required field missing/), - ).not.toBeInTheDocument(); - }); - - it("should show AJV validation errors", async () => { - const mockAjv = { - compile: jest.fn(() => ({ - errors: [{ instancePath: "/name", message: "should be string" }], - })), - errorsText: jest.fn(() => "data/name should be string"), - }; - - jest.doMock("ajv", () => jest.fn(() => mockAjv)); - - renderElicitationModal(); - - const input = screen.getByTestId("form-input"); - await act(async () => { - fireEvent.change(input, { target: { value: '{"name": "John"}' } }); - }); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - }); - }); - - describe("Email Validation", () => { - it("should accept valid email formats", async () => { - const validEmails = [ - "test@example.com", - "user.name@domain.co.uk", - "user+tag@example.org", - ]; - - for (const email of validEmails) { - renderElicitationModal(); - - const input = screen.getByTestId("form-input"); - await act(async () => { - fireEvent.change(input, { - target: { value: `{"name": "John", "email": "${email}"}` }, - }); - }); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect(mockResolve).toHaveBeenCalledWith({ - action: "accept", - content: { name: "John", email }, - }); - - jest.clearAllMocks(); - document.body.innerHTML = ""; - } - }); - - it("should reject invalid email formats", async () => { - const invalidEmails = [ - "invalid-email", - "@example.com", - "user@", - "user.example.com", - ]; - - for (const email of invalidEmails) { - renderElicitationModal(); - - const input = screen.getByTestId("form-input"); - await act(async () => { - fireEvent.change(input, { - target: { value: `{"name": "John", "email": "${email}"}` }, - }); - }); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect( - screen.getByText(/Invalid email format: email/), - ).toBeInTheDocument(); - expect(mockResolve).not.toHaveBeenCalledWith( - expect.objectContaining({ action: "accept" }), - ); - - jest.clearAllMocks(); - document.body.innerHTML = ""; - } - }); - }); - - describe("Schema Types", () => { - it("should handle string schema", () => { - const request = createMockRequest({ - requestedSchema: { - type: "object", - properties: { - username: { type: "string", description: "Username" }, - }, - }, - }); - renderElicitationModal(request); - expect(screen.getByTestId("dynamic-json-form")).toBeInTheDocument(); - }); - - it("should handle number schema", () => { - const request = createMockRequest({ - requestedSchema: { - type: "object", - properties: { - age: { type: "number", minimum: 0, maximum: 120 }, - }, - }, - }); - renderElicitationModal(request); - expect(screen.getByTestId("dynamic-json-form")).toBeInTheDocument(); - }); - - it("should handle boolean schema", () => { - const request = createMockRequest({ - requestedSchema: { - type: "object", - properties: { - subscribe: { - type: "boolean", - description: "Subscribe to newsletter", - }, - }, - }, - }); - renderElicitationModal(request); - expect(screen.getByTestId("dynamic-json-form")).toBeInTheDocument(); - }); - - it("should handle enum schema", () => { - const request = createMockRequest({ - requestedSchema: { - type: "object", - properties: { - country: { - type: "string", - enum: ["US", "UK", "CA"], - enumNames: ["United States", "United Kingdom", "Canada"], - }, - }, - }, - }); - renderElicitationModal(request); - expect(screen.getByTestId("dynamic-json-form")).toBeInTheDocument(); - }); - }); - - describe("Error Handling", () => { - it("should handle validation errors gracefully", async () => { - renderElicitationModal(); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect(screen.getByText(/Validation Error/)).toBeInTheDocument(); - }); - - it("should handle JSON parsing errors in form validation", async () => { - renderElicitationModal(); - - const input = screen.getByTestId("form-input"); - await act(async () => { - fireEvent.change(input, { target: { value: "invalid json" } }); - }); - - await act(async () => { - fireEvent.click(screen.getByTestId("button-submit")); - }); - - expect(mockResolve).not.toHaveBeenCalledWith( - expect.objectContaining({ action: "accept" }), - ); - }); - }); - - describe("Default Values", () => { - it("should generate default values when request changes", () => { - const { rerender } = renderElicitationModal(); - - const newRequest = createMockRequest({ - id: 2, - requestedSchema: { - type: "object", - properties: { - newField: { type: "string" }, - }, - }, - }); - - rerender(); - - expect(schemaUtils.generateDefaultValue).toHaveBeenCalledWith( - newRequest.requestedSchema, - ); - }); - }); -});