From 164c307bd1788f3cda73ca1383392afa299fbae6 Mon Sep 17 00:00:00 2001 From: murashit Date: Fri, 10 Jul 2026 13:21:14 +0900 Subject: [PATCH] Read Defuddle Markdown output from the content field --- .../host/obsidian/web-clipper.obsidian.ts | 2 +- .../chat/host/web-clipper.integration.test.ts | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/features/chat/host/web-clipper.integration.test.ts diff --git a/src/features/chat/host/obsidian/web-clipper.obsidian.ts b/src/features/chat/host/obsidian/web-clipper.obsidian.ts index 1d9be35b..4f41c6e5 100644 --- a/src/features/chat/host/obsidian/web-clipper.obsidian.ts +++ b/src/features/chat/host/obsidian/web-clipper.obsidian.ts @@ -79,7 +79,7 @@ async function defuddleUrl(options: VaultWebClipperOptions, url: string): Promis const result = new Defuddle(document, { url, markdown: true, useAsync: false }).parse(); return { title: result.title, - content: result.contentMarkdown ?? result.content, + content: result.content, site: result.site, domain: result.domain, }; diff --git a/tests/features/chat/host/web-clipper.integration.test.ts b/tests/features/chat/host/web-clipper.integration.test.ts new file mode 100644 index 00000000..04b8c37c --- /dev/null +++ b/tests/features/chat/host/web-clipper.integration.test.ts @@ -0,0 +1,73 @@ +// @vitest-environment jsdom + +import type { Vault } from "obsidian"; +import { describe, expect, it, vi } from "vitest"; + +import type { ComposerInputSnapshot } from "../../../../src/features/chat/application/composer/input-snapshot"; + +const mocks = vi.hoisted(() => ({ + requestUrl: vi.fn(), +})); + +vi.mock("obsidian", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + requestUrl: mocks.requestUrl, + }; +}); + +const { TFile } = await import("obsidian"); +const { createVaultWebClipper } = await import("../../../../src/features/chat/host/obsidian/web-clipper.obsidian"); + +describe("vault web clipper parser integration", () => { + it("extracts article HTML with Defuddle and saves it as Markdown", async () => { + mocks.requestUrl.mockResolvedValue({ + text: ` + + Integration Article + + +
+
+

Parser contract heading

+

This readable paragraph exercises the real Defuddle full browser bundle.

+

A second paragraph makes the article content unambiguous.

+
+
+ + `, + }); + const { vault, createdContent } = memoryVault(); + + const result = await createVaultWebClipper({ + vault, + settings: () => ({ clipFolder: "Clips", clipFilenameTemplate: "{{title}}.md", clipTags: "" }), + prepareInput: () => ({ text: "", input: [{ type: "text", text: "" }] }), + viewWindow: () => window, + now: () => new Date("2026-07-10T00:00:00.000Z"), + }).clipUrl("https://example.com/article", "", {} as ComposerInputSnapshot); + + const markdown = createdContent.get("Clips/Integration Article.md"); + expect(markdown).toContain("## Parser contract heading"); + expect(markdown).toContain("This readable paragraph exercises the real Defuddle full browser bundle."); + expect(markdown).not.toContain(" } { + const files = new Map>(); + const createdContent = new Map(); + const vault = { + getAbstractFileByPath: vi.fn((path: string) => files.get(path) ?? null), + createFolder: vi.fn().mockResolvedValue(undefined), + create: vi.fn().mockImplementation(async (path: string, content: string) => { + const File = TFile as unknown as new (path: string) => InstanceType; + files.set(path, new File(path)); + createdContent.set(path, content); + }), + }; + return { vault: vault as unknown as Vault, createdContent }; +}