import { fireEvent, render, screen } from "@testing-library/react" import { beforeEach, describe, expect, it, vi } from "vitest" import { BaseProviders } from "@/test/test-utils" import { ModalType } from "../../services/modal-machine" import { ModalContainer } from "../modal-container" // Mock useModal hook const mockCloseModal = vi.fn() const mockModalType = vi.fn() const mockModalData = vi.fn() const mockIsOpen = vi.fn() vi.mock("../../services", () => ({ useModal: () => ({ modalType: mockModalType(), modalData: mockModalData(), isOpen: mockIsOpen(), closeModal: mockCloseModal, }), })) // Mock all modal components vi.mock("@/features/app-state/components/missing-files-modal", () => ({ MissingFilesModal: () =>
Missing Files Modal
, })) vi.mock("@/features/camera-capture", () => ({ CameraCaptureModal: () =>
Camera Capture Modal
, })) vi.mock("@/features/color-grading/components/controls/color-grading-save-preset-modal", () => ({ ColorGradingSavePresetModal: () =>
Color Grading Modal
, })) vi.mock("@/features/effects/components/effect-detail-modal", () => ({ EffectDetailModal: () =>
Effect Detail Modal
, })) vi.mock("@/features/export", () => ({ ExportModal: () =>
Export Modal
, })) vi.mock("@/features/fairlight-audio/components/midi/midi-configuration-modal-component", () => ({ MidiConfigurationModalComponent: () =>
MIDI Configuration Modal
, })) vi.mock("@/features/fairlight-audio/components/midi/midi-learn-modal", () => ({ MidiLearnModal: () =>
MIDI Learn Modal
, })) vi.mock("@/features/fairlight-audio/components/midi/midi-mapping-editor-modal", () => ({ MidiMappingEditorModal: () =>
MIDI Mapping Modal
, })) vi.mock("@/features/keyboard-shortcuts", () => ({ KeyboardShortcutsModal: () =>
Keyboard Shortcuts Modal
, })) vi.mock("@/features/media/components/cache-settings-modal", () => ({ CacheSettingsModal: () =>
Cache Settings Modal
, })) vi.mock("@/features/person-identification/components/person-form-modal", () => ({ PersonFormModal: () =>
Person Form Modal
, })) vi.mock("@/features/project-settings", () => ({ ProjectSettingsModal: () =>
Project Settings Modal
, })) vi.mock("@/features/subtitles/components/subtitle-ai-tools-modal", () => ({ SubtitleAIToolsModal: () =>
Subtitle AI Tools Modal
, })) vi.mock("@/features/timeline/components/ai-markers/ai-marker-settings-modal", () => ({ AIMarkerSettingsModal: () =>
AI Marker Settings Modal
, })) vi.mock("@/features/timeline/components/audio-effects-editor-modal", () => ({ AudioEffectsEditorModal: () =>
Audio Effects Modal
, })) vi.mock("@/features/timeline/components/subtitle-editor-modal", () => ({ SubtitleEditorModal: () =>
Subtitle Editor Modal
, })) vi.mock("@/features/user-settings", () => ({ UserSettingsModal: () =>
User Settings Modal
, })) vi.mock("@/features/video-compiler/components/cache-statistics-modal", () => ({ CacheStatisticsModal: () =>
Cache Statistics Modal
, })) vi.mock("@/features/voice-recording", () => ({ VoiceRecordModal: () =>
Voice Recording Modal
, })) describe("ModalContainer", () => { beforeEach(() => { vi.clearAllMocks() mockModalType.mockReturnValue("none") mockModalData.mockReturnValue(null) mockIsOpen.mockReturnValue(false) }) it("should not render dialog when closed", () => { render( , ) expect(screen.queryByRole("dialog")).not.toBeInTheDocument() }) it("should render dialog when opened", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("project-settings") render( , ) expect(screen.getByRole("dialog")).toBeInTheDocument() expect(screen.getByText("modals.projectSettings.title")).toBeInTheDocument() }) it("should close modal on backdrop click", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("project-settings") render( , ) // Find the close button and click it const closeButton = screen.getByRole("button", { name: /close/i }) fireEvent.click(closeButton) expect(mockCloseModal).toHaveBeenCalled() }) describe("Modal Types", () => { const modalTestCases: Array<[ModalType, string, string]> = [ ["project-settings", "project-settings-modal", "modals.projectSettings.title"], ["keyboard-shortcuts", "keyboard-shortcuts-modal", "modals.keyboardShortcuts.title"], ["user-settings", "user-settings-modal", "modals.userSettings.title"], ["camera-capture", "camera-capture-modal", "modals.cameraCapture.title"], ["voice-recording", "voice-recording-modal", "modals.voiceRecording.title"], ["export", "export-modal", "modals.export.title"], ["cache-settings", "cache-settings-modal", "modals.cacheSettings.title"], ["cache-statistics", "cache-statistics-modal", "modals.cacheStatistics.title"], ["subtitle-editor", "subtitle-editor-modal", "modals.subtitleEditor.titleAdd"], ["person-form", "person-form-modal", "modals.personForm.titleAdd"], ["missing-files", "missing-files-modal", "modals.missingFiles.title"], ["ai-marker-settings", "ai-marker-settings-modal", "modals.aiMarkerSettings.title"], ["subtitle-ai-tools", "subtitle-ai-tools-modal", "modals.subtitleAITools.title"], ["audio-effects", "audio-effects-modal", "modals.audioEffects.title"], ["midi-learn", "midi-learn-modal", "modals.midiLearn.title"], ["midi-mapping", "midi-mapping-modal", "modals.midiMapping.title"], ["midi-configuration", "midi-configuration-modal", "modals.midiConfiguration.title"], ["effect-detail", "effect-detail-modal", "modals.effectDetail.title"], ["color-grading", "color-grading-modal", "modals.colorGrading.title"], ] it.each(modalTestCases)("should render %s modal with correct title", (modalType, testId, title) => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue(modalType) render( , ) expect(screen.getByTestId(testId)).toBeInTheDocument() expect(screen.getByText(title)).toBeInTheDocument() }) }) it("should handle subtitle editor with edit mode", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("subtitle-editor") mockModalData.mockReturnValue({ subtitle: { id: "1", text: "Test" } }) render( , ) expect(screen.getByText("modals.subtitleEditor.titleEdit")).toBeInTheDocument() }) it("should handle person form with edit mode", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("person-form") mockModalData.mockReturnValue({ person: { id: "1", name: "John" } }) render( , ) expect(screen.getByText("modals.personForm.titleEdit")).toBeInTheDocument() }) it("should apply custom dialog class from modalData", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("project-settings") mockModalData.mockReturnValue({ dialogClass: "custom-class" }) render( , ) const dialogElement = screen.getByRole("dialog") expect(dialogElement).toHaveClass("custom-class") }) describe("Dialog Classes", () => { const classTestCases: Array<[ModalType, string]> = [ ["camera-capture", "h-[max(600px,min(70vh,800px))]"], ["voice-recording", "h-[max(500px,min(60vh,700px))]"], ["export", "h-[max(700px,min(80vh,900px))]"], ["project-settings", "h-[450px]"], ["user-settings", "h-[700px]"], ["keyboard-shortcuts", "h-[max(600px,min(70vh,1000px))]"], ["cache-settings", "h-[max(700px,min(80vh,900px))]"], ["cache-statistics", "h-[max(600px,min(70vh,800px))]"], ["subtitle-editor", "h-[max(600px,min(70vh,800px))]"], ["person-form", "h-[max(500px,min(60vh,700px))]"], ["missing-files", "h-[max(600px,min(70vh,800px))]"], ["ai-marker-settings", "h-[max(600px,min(70vh,700px))]"], ["subtitle-ai-tools", "h-[max(500px,min(60vh,600px))]"], ["audio-effects", "max-w-3xl"], ["midi-learn", "sm:max-w-md"], ["midi-mapping", "sm:max-w-md"], ["midi-configuration", "max-w-2xl"], ["effect-detail", "max-w-4xl"], ["color-grading", "border-[#464647]"], ] it.each(classTestCases)("should apply correct class for %s modal", (modalType, expectedClass) => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue(modalType) render( , ) const dialogElement = screen.getByRole("dialog") expect(dialogElement).toHaveClass(expectedClass) }) }) it("should render nothing for unknown modal type", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("unknown-type" as ModalType) render( , ) // Should still render dialog but with no content expect(screen.getByRole("dialog")).toBeInTheDocument() expect(screen.queryByTestId(/modal$/)).not.toBeInTheDocument() }) it("should handle none modal type", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("none") render( , ) expect(screen.getByRole("dialog")).toBeInTheDocument() // Title should be empty const title = screen.getByRole("heading") expect(title.textContent).toBe("") }) it("should apply dark mode classes", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("project-settings") render( , ) const dialogElement = screen.getByRole("dialog") expect(dialogElement).toHaveClass("dark:bg-[#1e1e1e]") }) it("should have scrollable content area", () => { mockIsOpen.mockReturnValue(true) mockModalType.mockReturnValue("project-settings") render( , ) const contentArea = screen.getByTestId("project-settings-modal").parentElement expect(contentArea).toHaveClass("overflow-auto") }) })