diff --git a/apps/desktop/src/lib/__tests__/navicatImport.spec.ts b/apps/desktop/src/lib/__tests__/navicatImport.spec.ts
new file mode 100644
index 0000000000..b99b54c556
--- /dev/null
+++ b/apps/desktop/src/lib/__tests__/navicatImport.spec.ts
@@ -0,0 +1,87 @@
+import { describe, expect, it } from "vitest";
+import { parseNavicatConnections } from "../navicatImport";
+
+class TestElement {
+ readonly tagName: string;
+ readonly attributes: { name: string; value: string }[];
+ readonly children: TestElement[] = [];
+ readonly textContent = "";
+
+ constructor(tagName: string, attributes: { name: string; value: string }[]) {
+ this.tagName = tagName;
+ this.attributes = attributes;
+ }
+}
+
+class TestDocument {
+ private readonly elements: TestElement[];
+
+ constructor(xml: string) {
+ this.elements = Array.from(xml.matchAll(//gi)).map((match) => new TestElement("Connection", parseAttributes(match[1] || "")));
+ }
+
+ querySelector(selector: string) {
+ return selector === "parsererror" ? null : null;
+ }
+
+ querySelectorAll(selector: string) {
+ return selector === "*" ? this.elements : [];
+ }
+}
+
+class TestDOMParser {
+ parseFromString(xml: string) {
+ return new TestDocument(xml);
+ }
+}
+
+function parseAttributes(source: string) {
+ return Array.from(source.matchAll(/([^\s=]+)="([^"]*)"/g)).map((match) => ({ name: match[1] || "", value: match[2] || "" }));
+}
+
+if (!globalThis.DOMParser) {
+ globalThis.DOMParser = TestDOMParser as typeof DOMParser;
+}
+
+describe("parseNavicatConnections", () => {
+ it("imports SQLite DatabaseFile as both host and database", async () => {
+ const [connection] = await parseNavicatConnections(`
+
+`);
+
+ expect(connection?.db_type).toBe("sqlite");
+ expect(connection?.host).toBe("C:\\Users\\Yang\\demo.db");
+ expect(connection?.database).toBe("C:\\Users\\Yang\\demo.db");
+ expect(connection?.port).toBe(0);
+ });
+
+ it("imports SQLite numeric ConnType file name as host", async () => {
+ const [connection] = await parseNavicatConnections(`
+
+`);
+
+ expect(connection?.db_type).toBe("sqlite");
+ expect(connection?.host).toBe("/home/yang/demo.sqlite");
+ });
+
+ it("uses SQLite Database field as the file path", async () => {
+ const [connection] = await parseNavicatConnections(`
+
+`);
+
+ expect(connection?.db_type).toBe("sqlite");
+ expect(connection?.host).toBe("/tmp/app.data");
+ expect(connection?.database).toBe("/tmp/app.data");
+ });
+
+ it("keeps non-SQLite host and database mapping unchanged", async () => {
+ const [connection] = await parseNavicatConnections(`
+
+`);
+
+ expect(connection?.db_type).toBe("postgres");
+ expect(connection?.host).toBe("db.example.test");
+ expect(connection?.database).toBe("appdb");
+ expect(connection?.port).toBe(15432);
+ });
+});
diff --git a/apps/desktop/src/lib/navicatImport.ts b/apps/desktop/src/lib/navicatImport.ts
index fb10dd86ca..ca0712b912 100644
--- a/apps/desktop/src/lib/navicatImport.ts
+++ b/apps/desktop/src/lib/navicatImport.ts
@@ -54,6 +54,10 @@ function getAny(values: Record, keys: string[]) {
return "";
}
+function getSqlitePath(values: Record) {
+ return getAny(values, ["databaseFile", "databaseFileName", "databaseFilename", "filename", "fileName", "path", "databasePath", "dbPath", "dbFile", "sqliteFile", "sqlitePath", "database", "databaseName"]);
+}
+
function truthyNavicatFlag(value: string) {
const normalized = value.trim().toLowerCase();
return ["1", "true", "yes", "y", "on", "checked"].includes(normalized);
@@ -145,7 +149,7 @@ function isConnectionCandidate(node: ParsedNode) {
const type = getAny(node.values, ["type", "connType", "connectionType", "databaseType", "driver"]);
const name = getAny(node.values, ["name", "connectionName", "connName", "caption", "title"]);
const host = getAny(node.values, ["host", "server", "hostname", "serverHost", "address"]);
- const file = getAny(node.values, ["databaseFile", "filename", "path", "databasePath"]);
+ const file = getSqlitePath(node.values);
return !!(name || host || file) && !!(type || host || file);
}
@@ -174,9 +178,10 @@ async function parseConnection(node: ParsedNode): Promise