import { exerciseSubmissionInitialState, ExerciseSubmissionStateContext } from "@/components/Exercises/screens/Add/state/exerciseSubmissionState"; import { Step5Images } from "@/components/Exercises/screens/Add/Step5Images"; import { ImageFormData } from "@/components/Exercises/models/exercise"; import { ImageStyle } from "@/components/Exercises/models/image"; import { testQueryClient } from "@/tests/queryClient"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import React from "react"; const mockOnContinue = vi.fn(); const mockOnBack = vi.fn(); const initialImage: ImageFormData = { url: "blob://existing-image", file: new File([new Uint8Array([1, 2, 3])], "existing.png", { type: "image/png" }), author: "alice", authorUrl: "", title: "An existing image", derivativeSourceUrl: "", objectUrl: "", style: ImageStyle.PHOTO, isAi: false, }; function renderStep(initialImages: ImageFormData[] = []) { const queryClient = new QueryClient(); const dispatch = vi.fn(); const state = { ...exerciseSubmissionInitialState, images: initialImages }; render( ); return { dispatch }; } describe("Test the add exercise step 5 component", () => { beforeEach(() => { // jsdom doesn't define URL.createObjectURL; the file-upload handler relies on it if (!URL.createObjectURL) { URL.createObjectURL = vi.fn(() => "blob://mock"); } else { vi.spyOn(URL, "createObjectURL").mockReturnValue("blob://mock"); } }); afterEach(() => { vi.clearAllMocks(); vi.restoreAllMocks(); }); test("renders the upload prompt and continue/back buttons", () => { render( ); expect(screen.getByText("exercises.compatibleImagesCC")).toBeInTheDocument(); expect(screen.getByText("forms.supportedImageFormats")).toBeInTheDocument(); expect(screen.getByText("continue")).toBeInTheDocument(); expect(screen.getByText("goBack")).toBeInTheDocument(); }); test("clicking continue calls onContinue", async () => { const user = userEvent.setup(); renderStep(); await user.click(screen.getByText("continue")); expect(mockOnContinue).toHaveBeenCalled(); }); test("clicking goBack calls onBack", async () => { const user = userEvent.setup(); renderStep(); await user.click(screen.getByText("goBack")); expect(mockOnBack).toHaveBeenCalled(); }); test("renders existing images from state with their title and author", () => { renderStep([initialImage]); expect(screen.getByText("An existing image")).toBeInTheDocument(); expect(screen.getByText("alice")).toBeInTheDocument(); // The img has alt="" so it's decorative and not in the a11y tree; // grab it by tag and check the src. const imgs = document.querySelectorAll("img"); const matching = Array.from(imgs).find(i => i.src.includes(initialImage.url)); expect(matching).toBeDefined(); }); test("clicking 'Add image' opens the image modal", async () => { const user = userEvent.setup(); renderStep(); expect(screen.queryByText("exercises.imageDetails")).not.toBeInTheDocument(); await user.click(screen.getByText("exercises.addImage")); expect(await screen.findByText("exercises.imageDetails")).toBeInTheDocument(); }); });