test: add DOM methods to leftover test windows

This commit is contained in:
tianma-if committed 2026-09-30 22:12:09 +08:00
1 parent 6189a3ec56
commit f7d7e8e72c
6 files changed
+70 -19

No files matched your search

@@ -1,6 +1,7 @@
import { describe, expect, test } from "bun:test";
import { Editor } from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit";
import { ensureTestWindowDom } from "../../lib/restore-test-global.mjs";
import {
filterNoteLinkSuggestions,
insertSuggestedNoteLink,
@@ -31,6 +32,7 @@ describe("note link suggestion", () => {
});
test("replaces the @ query with the existing internal-note link format", () => {
ensureTestWindowDom();
const editor = new Editor({
extensions: [StarterKit],
content: {
@@ -64,6 +66,7 @@ describe("note link suggestion", () => {
});
test("uses the untitled fallback for an empty title", () => {
ensureTestWindowDom();
const editor = new Editor({
extensions: [StarterKit],
content: {
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
import { Editor } from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit";
import { FileAttachment, PdfAttachment } from "@edgeever/shared";
import { ensureTestWindowDom } from "../../lib/restore-test-global.mjs";
import { findAttachmentRange, removeAttachmentAt, renameAttachmentAt } from "./attachment-editor-range.ts";
const videoUrl = "edgeever-resource://resource/res_video";
@@ -35,10 +36,13 @@ const pdfNode = (url, filename) => ({
const paragraph = (...content) => ({ type: "paragraph", content });
const makeEditor = (content) => new Editor({
extensions: [StarterKit, FileAttachment, PdfAttachment],
content: { type: "doc", content },
});
const makeEditor = (content) => {
ensureTestWindowDom();
return new Editor({
extensions: [StarterKit, FileAttachment, PdfAttachment],
content: { type: "doc", content },
});
};
describe("attachment editor range", () => {
test("removes a video file-attachment node whose hover URL is the API blob path", () => {
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
import { Editor } from "@tiptap/core";
import { createEdgeEverDocumentExtensions } from "@edgeever/shared";
import { createEdgeEverMathematics } from "@edgeever/shared/mathematics";
import { ensureTestWindowDom } from "../../lib/restore-test-global.mjs";
import {
applyMathFormula,
deleteMathFormula,
@@ -9,12 +10,15 @@ import {
selectedTextAsLatex,
} from "./math-formula.ts";
const createEditor = (content) => new Editor({
extensions: createEdgeEverDocumentExtensions({
mathematics: createEdgeEverMathematics(),
}),
content,
});
const createEditor = (content) => {
ensureTestWindowDom();
return new Editor({
extensions: createEdgeEverDocumentExtensions({
mathematics: createEdgeEverMathematics(),
}),
content,
});
};
describe("math formula commands", () => {
test("inserts inline math at the caret", () => {
@@ -20,9 +20,16 @@ describe("PDF document sources", () => {
});
test("recovers leaked desktop resource URLs in the web runtime", async () => {
await expect(loadPdfDocumentSource("edgeever-resource://resource/res_web", undefined)).resolves.toEqual({
url: "/api/v1/resources/res_web/blob",
});
const current = globalThis.window;
const previousBridge = current?.edgeeverDesktop;
if (current) current.edgeeverDesktop = undefined;
try {
await expect(loadPdfDocumentSource("edgeever-resource://resource/res_web")).resolves.toEqual({
url: "/api/v1/resources/res_web/blob",
});
} finally {
if (current) current.edgeeverDesktop = previousBridge;
}
});
test("loads synced desktop resources through the native bridge", async () => {
+23 -5
View File
@@ -1,4 +1,20 @@
const restoreUnconditionally = Symbol("restore-unconditionally");
const noop = () => {};
// Linux Bun exposes `document` without a `window` binding. Test files then
// install plain objects. ProseMirror and TipTap read `window.MutationObserver`
// and `window.addEventListener` from that object, so a leftover stub needs
// those methods. Do not create a window when none is installed: assigning one
// is unnecessary on hosts whose editor already resolves `window` elsewhere.
export const ensureTestWindowDom = () => {
const current = globalThis.window;
if (!current || typeof current !== "object") return;
if (typeof current.addEventListener !== "function") current.addEventListener = noop;
if (typeof current.removeEventListener !== "function") current.removeEventListener = noop;
if (typeof current.getComputedStyle !== "function") current.getComputedStyle = () => ({ listStyle: "" });
if (typeof current.setTimeout !== "function") current.setTimeout = globalThis.setTimeout.bind(globalThis);
if (typeof current.clearTimeout !== "function") current.clearTimeout = globalThis.clearTimeout.bind(globalThis);
};
export const restoreTestGlobal = (key, original, installed = restoreUnconditionally) => {
if (installed !== restoreUnconditionally && globalThis[key] !== installed) return;
@@ -6,10 +22,12 @@ export const restoreTestGlobal = (key, original, installed = restoreUnconditiona
globalThis[key] = original;
return;
}
// Linux Bun provides document without a window binding. Assigning
// undefined shadows it, and deleting it makes ProseMirror throw
// `window is not defined` while reading MutationObserver. Keep the
// stub object when there was no previous window.
if (key === "window") return;
// Assigning undefined shadows the host binding, and deleting window makes
// ProseMirror throw `window is not defined`. Keep the stub and make it
// safe for a later editor test.
if (key === "window") {
ensureTestWindowDom();
return;
}
delete globalThis[key];
};
+16 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from "bun:test";
import { restoreTestGlobal } from "./restore-test-global.mjs";
import { ensureTestWindowDom, restoreTestGlobal } from "./restore-test-global.mjs";
test("restoreTestGlobal keeps a window object when nothing was there before", () => {
const descriptor = Object.getOwnPropertyDescriptor(globalThis, "window");
@@ -8,6 +8,8 @@ test("restoreTestGlobal keeps a window object when nothing was there before", ()
globalThis.window = stub;
restoreTestGlobal("window", undefined, stub);
expect(globalThis.window).toBe(stub);
expect(typeof globalThis.window.addEventListener).toBe("function");
expect(typeof globalThis.window.removeEventListener).toBe("function");
if (descriptor) Object.defineProperty(globalThis, "window", descriptor);
else delete globalThis.window;
});
@@ -23,3 +25,16 @@ test("restoreTestGlobal leaves a global that another file replaced", () => {
if (descriptor) Object.defineProperty(globalThis, "document", descriptor);
else delete globalThis.document;
});
test("ensureTestWindowDom fills event methods on a leftover window", () => {
const descriptor = Object.getOwnPropertyDescriptor(globalThis, "window");
const stub = { edgeeverDesktop: { isAvailable: true } };
globalThis.window = stub;
ensureTestWindowDom();
expect(typeof stub.addEventListener).toBe("function");
expect(typeof stub.getComputedStyle).toBe("function");
expect(stub.edgeeverDesktop.isAvailable).toBe(true);
ensureTestWindowDom();
if (descriptor) Object.defineProperty(globalThis, "window", descriptor);
else delete globalThis.window;
});