import { act, assertEquals, cleanup, render } from "./setup.ts";
import { EditorMode } from "../../islands/Editor.tsx";
import Editor from "../../islands/Editor.tsx";
function setupMocks() {
const clickSpy: { href: string; download: string }[] = [];
const createObjectURLCalls: string[] = [];
let revokeObjectURLCalled = false;
const originalCreateElement = document.createElement.bind(document);
document.createElement = (tag: string) => {
const el = originalCreateElement(tag) as HTMLAnchorElement;
if (tag === "a") {
el.click = () => {
clickSpy.push({ href: el.href, download: el.download });
};
}
return el;
};
const origCreateObjectURL = URL.createObjectURL;
const origRevokeObjectURL = URL.revokeObjectURL;
URL.createObjectURL = () => {
createObjectURLCalls.push("called");
return "blob:fake-url";
};
URL.revokeObjectURL = () => {
revokeObjectURLCalled = true;
};
const origFetch = globalThis.fetch;
globalThis.fetch = (() =>
Promise.resolve(
new Response("/* mock css */", {
status: 200,
headers: { "Content-Type": "text/css" },
}),
)) as typeof fetch;
const listeners: { type: string; listener: EventListener }[] = [];
const origAddEventListener = globalThis.addEventListener;
const origRemoveEventListener = globalThis.removeEventListener;
globalThis.addEventListener = (
type: string,
listener: EventListenerOrEventListenerObject,
) => {
listeners.push({
type,
listener: listener as EventListener,
});
return origAddEventListener.call(globalThis, type, listener);
};
globalThis.removeEventListener = (
type: string,
listener: EventListenerOrEventListenerObject,
) => {
const idx = listeners.findIndex((l) =>
l.type === type && l.listener === (listener as EventListener)
);
if (idx !== -1) listeners.splice(idx, 1);
return origRemoveEventListener.call(globalThis, type, listener);
};
const emitEvent = (type: string, detail: string) => {
const match = listeners.filter((l) => l.type === type);
for (const { listener } of match) {
listener({ type, detail } as unknown as Event);
}
};
return {
clickSpy,
createObjectURLCalls,
get revokeObjectURLCalled() {
return revokeObjectURLCalled;
},
emitEvent,
getListenerCount(type: string) {
return listeners.filter((l) => l.type === type).length;
},
restore() {
document.createElement = originalCreateElement;
URL.createObjectURL = origCreateObjectURL;
URL.revokeObjectURL = origRevokeObjectURL;
globalThis.fetch = origFetch;
globalThis.addEventListener = origAddEventListener;
globalThis.removeEventListener = origRemoveEventListener;
},
};
}
Deno.test({
name:
"Editor - DownloadRequest event triggers download with correct filename",
fn() {
const mocks = setupMocks();
render();
act(() => {
mocks.emitEvent("DownloadRequest", "My Post");
});
assertEquals(mocks.clickSpy.length, 1);
assertEquals(mocks.clickSpy[0].download, "My Post.md");
assertEquals(mocks.createObjectURLCalls.length, 1);
assertEquals(mocks.revokeObjectURLCalled, true);
mocks.restore();
cleanup();
},
sanitizeResources: false,
sanitizeOps: false,
});
Deno.test({
name: "Editor - DownloadRequest uses Untitled for empty title",
fn() {
const mocks = setupMocks();
render(
,
);
act(() => {
mocks.emitEvent("DownloadRequest", "");
});
assertEquals(mocks.clickSpy.length, 1);
assertEquals(mocks.clickSpy[0].download, "Untitled.md");
mocks.restore();
cleanup();
},
sanitizeResources: false,
sanitizeOps: false,
});
Deno.test({
name: "Editor - removes DownloadRequest listener on unmount",
fn() {
const mocks = setupMocks();
const { unmount } = render(
,
);
assertEquals(mocks.getListenerCount("DownloadRequest"), 1);
act(() => {
unmount();
});
assertEquals(mocks.getListenerCount("DownloadRequest"), 0);
mocks.restore();
cleanup();
},
sanitizeResources: false,
sanitizeOps: false,
});