import React from "react"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import ImportExportJobs from "./components/ImportExportJobs"; import { I18nProvider } from "./i18n/I18nProvider"; import { ToastProvider } from "./toast"; import { api } from "./api"; const mockedApi = api as jest.Mocked; const readBytes = (blob: Blob) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(new Uint8Array(reader.result as ArrayBuffer)); reader.onerror = reject; reader.readAsArrayBuffer(blob); }); afterEach(() => jest.restoreAllMocks()); test.each(["csv", "json"])("%s download preserves server bytes and a generated safe filename", async (format) => { const bytes = new TextEncoder().encode(format === "csv" ? "\ufeffCompany,JobTitle\r\nÆrlig,'=1+1\r\n" : '[{"jobTitle":"=1+1","company":"Ærlig"}]'); const incoming = new Blob([bytes]); mockedApi.get.mockResolvedValue({ data: incoming }); const createUrl = jest.fn(() => "blob:csv-export"); Object.defineProperty(URL, "createObjectURL", { configurable: true, value: createUrl }); Object.defineProperty(URL, "revokeObjectURL", { configurable: true, value: jest.fn() }); let filename = ""; jest.spyOn(HTMLAnchorElement.prototype, "click").mockImplementation(function (this: HTMLAnchorElement) { filename = this.download; }); render(); fireEvent.click(screen.getByRole("button", { name: `Export ${format.toUpperCase()}` })); await waitFor(() => expect(createUrl).toHaveBeenCalledTimes(1)); expect(mockedApi.get).toHaveBeenCalledWith(`/export/jobs?format=${format}&includeDeleted=false`, { responseType: "blob" }); expect(await readBytes(createUrl.mock.calls[0][0])).toEqual(bytes); expect(filename).toMatch(new RegExp(`^job-tracker-export-\\d{4}-\\d{2}-\\d{2}\\.${format}$`)); });