import { render, screen, fireEvent, waitFor, cleanup, } from "@testing-library/react"; import assert from "node:assert"; import { describe, test, beforeEach, mock, afterEach, } from "node:test"; import type { TOnFileDelete } from "./config/types"; import { FilesUploader } from "./index"; describe("FilesUploader", () => { // Keep references to console spies for cleanup let consoleErrorSpy: ReturnType; let consoleWarnSpy: ReturnType; afterEach(() => { cleanup(); }); beforeEach(() => { // Restore previous spies if they exist if (consoleErrorSpy) { consoleErrorSpy.mock.restore(); } if (consoleWarnSpy) { consoleWarnSpy.mock.restore(); } // Silence console.error and console.warn consoleErrorSpy = mock.method(console, "error", () => {}); consoleWarnSpy = mock.method(console, "warn", () => {}); }); describe("rendering", () => { test("renders with default props", () => { const { container } = render(); const hiddenInput = container.querySelector('input[type="file"][name="files"]'); const visibleInput = container.querySelector('label input[type="file"]'); assert.notStrictEqual(hiddenInput, null); assert.notStrictEqual(visibleInput, null); assert.notStrictEqual(screen.queryByRole("tooltip"), null); }); test("passes extra HTML attributes", () => { const { container } = render( ); assert.notStrictEqual(container.querySelector('[data-testid="uploader"]'), null); assert.notStrictEqual(container.querySelector('[id="uploader"]'), null); }); }); describe("configuration", () => { test("sets 'name' prop correctly", () => { const { container } = render(); const hiddenInput = container.querySelector('input[type="file"][name="documents"]'); assert.notStrictEqual(hiddenInput, null); assert.strictEqual(hiddenInput?.getAttribute("name"), "documents"); }); test("sets 'id' prop correctly", () => { const { container } = render(); const hiddenInput = container.querySelector('input[type="file"]#file-upload'); assert.notStrictEqual(hiddenInput, null); assert.strictEqual(hiddenInput?.getAttribute("id"), "file-upload"); }); test("sets 'form' prop correctly", () => { const { container } = render(); const hiddenInput = container.querySelector('input[type="file"][name="files"]'); assert.strictEqual(hiddenInput?.getAttribute("form"), "myForm"); }); test("sets 'isRequired' prop correctly", () => { const { container } = render(); const hiddenInput = container.querySelector('input[type="file"][name="files"]'); assert.strictEqual(hiddenInput?.getAttribute("data-js-input-required"), "file"); }); test("sets 'multiple' attribute correctly", () => { const { container } = render(); const visibleInput = container.querySelector('label input[type="file"]'); assert.ok(visibleInput?.hasAttribute("multiple")); }); test("sets 'accept' attribute correctly", () => { const { container } = render(); const visibleInput = container.querySelector('label input[type="file"]'); const accept = visibleInput?.getAttribute("accept"); assert.ok(accept?.includes(".pdf")); assert.ok(accept?.includes(".doc")); }); }); describe("disabled state", () => { test("disables input when isDisabled=true", () => { const { container } = render(); const visibleInput = container.querySelector('label input[type="file"]') as HTMLInputElement; assert.strictEqual(visibleInput.disabled, true); }); test("sets isDisabled css-class", () => { const { container } = render(); assert.notStrictEqual(container.querySelector(".filesUploader--isDisabled"), null); }); test("does not delete files when is disabled", async () => { const onFileDelete = mock.fn(); const { rerender } = render( ); // Wait for file to be displayed await waitFor(() => { assert.notStrictEqual(screen.queryByText("existing.pdf"), null); }); // Re-render with disabled state rerender( ); const deleteButton = screen.getByRole("button") as HTMLButtonElement; assert.strictEqual(deleteButton.disabled, true); fireEvent.click(deleteButton); assert.strictEqual(onFileDelete.mock.calls.length, 0); }); }); });