From 6fe20639702e3eb81bd262075094fb5d1f7033b9 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sat, 3 Jan 2026 10:39:45 +0000 Subject: fix: Eliminate the O(n2) parsing of the netscape import parsing (#2338) * fix: Eliminate the O(n2) parsing of the netscape import parsing * remove unneeded tests --- packages/shared/import-export/parsers.test.ts | 301 ++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 packages/shared/import-export/parsers.test.ts (limited to 'packages/shared/import-export/parsers.test.ts') diff --git a/packages/shared/import-export/parsers.test.ts b/packages/shared/import-export/parsers.test.ts new file mode 100644 index 00000000..18502305 --- /dev/null +++ b/packages/shared/import-export/parsers.test.ts @@ -0,0 +1,301 @@ +import { describe, expect, it } from "vitest"; + +import { parseImportFile } from "./parsers"; + +describe("parseNetscapeBookmarkFile", () => { + it("parses a simple bookmark file with single bookmark", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Example Site +

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + title: "Example Site", + content: { + type: "link", + url: "https://example.com", + }, + tags: [], + addDate: 1234567890, + paths: [[]], + }); + }); + + it("parses bookmarks with tags", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Example Site +

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0].tags).toEqual(["tag1", "tag2", "tag3"]); + }); + + it("parses bookmarks in nested folders", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Folder1

+

+

Folder2

+

+

Nested Bookmark +

+

+

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + title: "Nested Bookmark", + content: { + type: "link", + url: "https://example.com", + }, + paths: [["Folder1", "Folder2"]], + }); + }); + + it("handles empty folder names by replacing with 'Unnamed'", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Named Folder

+

+

+

+

Bookmark +

+

+

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0].paths).toEqual([["Named Folder", "Unnamed"]]); + }); + + it("parses multiple bookmarks in different folders", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Tech

+

+

GitHub +
Stack Overflow +

+

News

+

+

Hacker News +

+

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(3); + + expect(result[0]).toMatchObject({ + title: "GitHub", + content: { type: "link", url: "https://github.com" }, + paths: [["Tech"]], + }); + + expect(result[1]).toMatchObject({ + title: "Stack Overflow", + content: { type: "link", url: "https://stackoverflow.com" }, + paths: [["Tech"]], + }); + + expect(result[2]).toMatchObject({ + title: "Hacker News", + content: { type: "link", url: "https://news.ycombinator.com" }, + paths: [["News"]], + }); + }); + + it("parses bookmarks at root level (no folders)", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Bookmark 1 +
Bookmark 2 +

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(2); + expect(result[0].paths).toEqual([[]]); + expect(result[1].paths).toEqual([[]]); + }); + + it("handles deeply nested folder structures", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Level1

+

+

Level2

+

+

Level3

+

+

Level4

+

+

Deep Bookmark +

+

+

+

+

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0].paths).toEqual([["Level1", "Level2", "Level3", "Level4"]]); + }); + + it("deduplicates bookmarks with the same URL", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Folder1

+

+

First Instance +

+

Folder2

+

+

Second Instance +

+

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + content: { type: "link", url: "https://example.com" }, + tags: ["tag1", "tag2"], + addDate: 1234567890, // Should keep the earlier date + }); + expect(result[0].paths).toHaveLength(2); + expect(result[0].paths).toContainEqual(["Folder1"]); + expect(result[0].paths).toContainEqual(["Folder2"]); + }); + + it("merges notes from duplicate bookmarks", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Bookmark +
First note +
Bookmark +
Second note +

`; + + // Note: The current parser doesn't extract DD notes, but this test + // documents the expected behavior if/when DD parsing is added + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0].content).toMatchObject({ + type: "link", + url: "https://example.com", + }); + }); + + it("handles bookmarks without ADD_DATE attribute", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

No Date Bookmark +

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0].addDate).toBeUndefined(); + }); + + it("handles bookmarks without HREF attribute", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

No URL Bookmark +

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(1); + expect(result[0].content).toBeUndefined(); + }); + + it("handles mixed structure with folders and root-level bookmarks", () => { + const html = ` + +Bookmarks +

Bookmarks

+

+

Root Bookmark 1 +

Folder

+

+

Folder Bookmark +

+

Root Bookmark 2 +

`; + + const result = parseImportFile("html", html); + + expect(result).toHaveLength(3); + expect(result[0]).toMatchObject({ + title: "Root Bookmark 1", + paths: [[]], + }); + expect(result[1]).toMatchObject({ + title: "Folder Bookmark", + paths: [["Folder"]], + }); + expect(result[2]).toMatchObject({ + title: "Root Bookmark 2", + paths: [[]], + }); + }); + + it("throws error for non-Netscape bookmark files", () => { + const html = ` +Not a bookmark file +Just a regular HTML file +`; + + expect(() => parseImportFile("html", html)).toThrow( + "The uploaded html file does not seem to be a bookmark file", + ); + }); +}); -- cgit v1.2.3-70-g09d2