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