fix(sidebar): preserve objects for ancestor search matches

Closes #5613
This commit is contained in:
t8y2 committed 2026-08-13 12:15:46 +00:00
1 parent cce36ae058
commit e0a7bc996d
9 files changed
+282 -41

No files matched your search

@@ -2968,17 +2968,15 @@ describe("connectionStore metadata loading", () => {
}));
let resolveSearchClearReload!: (tables: TableInfo[]) => void;
let unfilteredFirstPageCalls = 0;
const listTables = vi.fn(
(_connectionId: string, _database: string, _schema: string, searchFilter?: string, limit?: number, offset?: number) => {
if (searchFilter) return Promise.resolve(tables.filter((table) => table.name.includes(searchFilter)));
if ((offset ?? 0) === 0 && ++unfilteredFirstPageCalls === 1) {
return new Promise<TableInfo[]>((resolve) => {
resolveSearchClearReload = resolve;
});
}
return Promise.resolve(tables.slice(offset ?? 0, (offset ?? 0) + (limit ?? tables.length)));
},
);
const listTables = vi.fn((_connectionId: string, _database: string, _schema: string, searchFilter?: string, limit?: number, offset?: number) => {
if (searchFilter) return Promise.resolve(tables.filter((table) => table.name.includes(searchFilter)));
if ((offset ?? 0) === 0 && ++unfilteredFirstPageCalls === 1) {
return new Promise<TableInfo[]>((resolve) => {
resolveSearchClearReload = resolve;
});
}
return Promise.resolve(tables.slice(offset ?? 0, (offset ?? 0) + (limit ?? tables.length)));
});
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
vi.doMock("@/lib/backend/api", () => ({
@@ -3060,6 +3058,75 @@ describe("connectionStore metadata loading", () => {
expect(listTables).toHaveBeenCalledTimes(5);
});
it("keeps connection-name matches out of object metadata filters", async () => {
const listTables = vi.fn((_connectionId: string, _database: string, _schema: string, searchFilter?: string) => Promise.resolve(searchFilter === "orders" || !searchFilter ? [{ name: "orders", table_type: "TABLE" as const, comment: null }] : []));
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
vi.doMock("@/lib/backend/api", () => ({
checkConnectionHealth: vi.fn().mockResolvedValue(undefined),
deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined),
listTables,
loadSchemaCache: vi.fn().mockResolvedValue(null),
saveConnections: vi.fn().mockResolvedValue(undefined),
saveSchemaCache: vi.fn().mockResolvedValue(undefined),
saveSidebarLayout: vi.fn().mockResolvedValue(undefined),
}));
const { useConnectionStore } = await import("@/stores/connectionStore");
const { useSettingsStore } = await import("@/stores/settingsStore");
const store = useConnectionStore();
const settingsStore = useSettingsStore();
settingsStore.editorSettings.sidebarObjectDisplay = "grouped";
settingsStore.desktopSettings.sidebar_table_page_size = 200;
const connection = { ...mysqlConnection(), name: "60307" };
const tablesGroup: TreeNode = {
id: "mysql-1:basic:__tables",
label: "tree.tables",
type: "group-tables",
connectionId: connection.id,
database: "basic",
isExpanded: true,
children: [],
};
store.connections = [connection];
store.connectedIds.add(connection.id);
store.treeNodes = [{ id: connection.id, label: connection.name, type: "connection", connectionId: connection.id, children: [tablesGroup] }];
store.sidebarSearchQuery = "60307";
await store.loadObjectGroupChildren(tablesGroup, { force: true, searchFilter: "", allowGlobalSearchMismatch: true, expectedSidebarSearchQuery: "60307" });
expect(listTables).toHaveBeenLastCalledWith(connection.id, "basic", "basic", undefined, 201, 0, ["TABLE"]);
expect(tablesGroup.children?.map((node) => node.label)).toEqual(["orders"]);
store.sidebarSearchQuery = "orders";
await store.loadObjectGroupChildren(tablesGroup, { force: true });
expect(listTables).toHaveBeenLastCalledWith(connection.id, "basic", "basic", "orders", 200, undefined, ["TABLE"]);
expect(tablesGroup.children?.map((node) => node.label)).toEqual(["orders"]);
let resolveAncestorLoad!: (tables: TableInfo[]) => void;
listTables.mockImplementationOnce(
() =>
new Promise<TableInfo[]>((resolve) => {
resolveAncestorLoad = resolve;
}),
);
store.sidebarSearchQuery = "60307";
const ancestorLoad = store.loadObjectGroupChildren(tablesGroup, {
force: true,
searchFilter: "",
allowGlobalSearchMismatch: true,
expectedSidebarSearchQuery: "60307",
});
await vi.waitFor(() => expect(listTables).toHaveBeenCalledTimes(3));
store.sidebarSearchQuery = "customers";
resolveAncestorLoad([{ name: "stale_table", table_type: "TABLE", comment: null }]);
await ancestorLoad;
expect(tablesGroup.children?.map((node) => node.label)).toEqual(["orders"]);
});
it("merges load-more table pages onto the live group node after an in-tree replacement", async () => {
const firstPage = Array.from({ length: 201 }, (_, index) => ({
name: `t_${String(index + 1).padStart(4, "0")}`,
+4 -3
View File
@@ -2176,8 +2176,9 @@ export const useConnectionStore = defineStore("connection", () => {
function isTreeLoadSearchChanged(searchFilter: string, options?: LoadTreeOptions): boolean {
if (options?.sidebarTableSearchParentId) return isSidebarTableSearchQueryChanged(options);
if (isSidebarSearchQueryChanged(options)) return true;
if (options?.allowGlobalSearchMismatch) return false;
return (sidebarSearchQuery.value || "") !== searchFilter || isSidebarSearchQueryChanged(options);
return (sidebarSearchQuery.value || "") !== searchFilter;
}
function isTreeNodeChildrenLoaded(nodeId: string): boolean {
@@ -4476,7 +4477,7 @@ export const useConnectionStore = defineStore("connection", () => {
nonTableObjectTypes,
offset: 0,
pageSize,
searchFilter: searchFilter || undefined,
searchFilter: options?.searchFilter === "" ? "" : searchFilter || undefined,
force: options?.force,
});
children = page.hasMore && !searchFilter ? appendTableTreeLoadMoreNode(page.children, buildLoadMoreNode(node, page.nextOffset, pageSize), page.loadMoreParent) : page.children;
@@ -4614,7 +4615,7 @@ export const useConnectionStore = defineStore("connection", () => {
objectTypes,
offset: 0,
pageSize: sidebarObjectGroupPageSize(),
searchFilter: searchFilter || undefined,
searchFilter: options?.searchFilter === "" ? "" : searchFilter || undefined,
force: options?.force,
});
children = page.hasMore && !searchFilter ? appendTableTreeLoadMoreNode(page.children, buildLoadMoreNode(node, page.nextOffset, sidebarObjectGroupPageSize()), page.loadMoreParent) : page.children;