diff --git a/src/modules/features/SettingDialogue/PanePatches.ts b/src/modules/features/SettingDialogue/PanePatches.ts index d879ed9d..4a2ed985 100644 --- a/src/modules/features/SettingDialogue/PanePatches.ts +++ b/src/modules/features/SettingDialogue/PanePatches.ts @@ -188,7 +188,8 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen } this.requestUpdate(); }; - text.inputEl.before((dateEl = activeDocument.createSpan())); + dateEl = text.inputEl.ownerDocument.createElement("span"); + text.inputEl.before(dateEl); text.inputEl.type = "datetime-local"; if (this.editingSettings.maxMTimeForReflectEvents > 0) { const date = new Date(this.editingSettings.maxMTimeForReflectEvents); diff --git a/src/modules/features/SettingDialogue/PanePatches.unit.spec.ts b/src/modules/features/SettingDialogue/PanePatches.unit.spec.ts new file mode 100644 index 00000000..f489b054 --- /dev/null +++ b/src/modules/features/SettingDialogue/PanePatches.unit.spec.ts @@ -0,0 +1,98 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { panePatches } from "./PanePatches.ts"; + +const remediationHarness = vi.hoisted(() => { + const dateElement = { textContent: "" }; + const createElement = vi.fn(() => dateElement); + const before = vi.fn(); + const inputEl = { + before, + ownerDocument: { createElement }, + type: "", + }; + const textComponent = { + inputEl, + onChange: vi.fn(), + setValue: vi.fn(), + }; + + return { + before, + createElement, + dateElement, + inputEl, + textComponent, + }; +}); + +vi.mock("./LiveSyncSetting.ts", () => ({ + LiveSyncSetting: class LiveSyncSetting { + addText(callback: (text: typeof remediationHarness.textComponent) => void): this { + callback(remediationHarness.textComponent); + return this; + } + + setAuto(): this { + return this; + } + + addApplyButton(): this { + return this; + } + + autoWireToggle(): this { + return this; + } + }, +})); + +afterEach(() => { + Reflect.deleteProperty(globalThis, "activeDocument"); + vi.clearAllMocks(); + remediationHarness.dateElement.textContent = ""; + remediationHarness.inputEl.type = ""; +}); + +describe("panePatches remediation setting", () => { + it("creates the status element without appending it to the document", () => { + const hierarchyError = new DOMException( + "Failed to execute 'appendChild' on 'Node': Only one element on document allowed.", + "HierarchyRequestError" + ); + const createSpan = vi.fn(() => { + throw hierarchyError; + }); + Object.defineProperty(globalThis, "activeDocument", { + configurable: true, + value: { createSpan }, + }); + + const host = { + addOnSaved: vi.fn(), + editingSettings: { + maxMTimeForReflectEvents: 0, + }, + requestUpdate: vi.fn(), + }; + const addPanel = vi.fn((_paneEl: HTMLElement, title: string) => ({ + then(callback: (paneEl: HTMLElement) => void) { + if (title === "Remediation") { + callback({} as HTMLElement); + } + return Promise.resolve(); + }, + })); + + panePatches.call( + host as never, + {} as HTMLElement, + { + addPanel, + } as never + ); + expect(createSpan).not.toHaveBeenCalled(); + expect(remediationHarness.createElement).toHaveBeenCalledWith("span"); + expect(remediationHarness.before).toHaveBeenCalledWith(remediationHarness.dateElement); + expect(remediationHarness.dateElement.textContent).toBe("No limit configured"); + }); +});