From e65dfe81765105d19ab0569e0f041f5f8e917d39 Mon Sep 17 00:00:00 2001 From: Chuan-kai Lin Date: Fri, 17 Jul 2026 14:42:52 -0700 Subject: [PATCH] Fix flaky query history reveal test via stable TreeItem ids The query history tree data provider never set an id on its TreeItems, so VS Code identified tree nodes by their label plus their position in the list. When multiple history items share the same label, that positional identity is ambiguous, so treeView.reveal could resolve to the wrong item or fail outright ("Data tree node not found") when the list was re-sorted or refreshed. This mainly affects tests. The default label format includes the query start time, so real query history labels are effectively unique and users are unlikely to hit this (the only exception being a user who customises the label format to something non-unique). In query-history-manager.test.ts the label format is overridden to just the query name, producing identical labels; this made the "should not change the selection" cases flaky, reproducing locally around 50% of the time and disappearing entirely after this change. Because the impact is effectively test-only, no changelog entry is added. Set a stable, unique id on each tree item so reveal resolves deterministically regardless of duplicate labels or sort order. The id must be unique per item: getQueryId is not sufficient on its own because a multi-query run produces several local-query items that share the same initialInfo.id, so for local queries we also include the per-result output base name (VS Code de-duplicates nodes that share an id, which would otherwise break reveal/selection for the colliding items). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f0809f2f-2c92-4acf-8f51-468c5e79c14a --- .../history-tree-data-provider.ts | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts b/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts index 7b355539a9a..8d1f43dd62f 100644 --- a/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts +++ b/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts @@ -3,7 +3,7 @@ import { env, EventEmitter, ThemeColor, ThemeIcon, TreeItem } from "vscode"; import { DisposableObject } from "../common/disposable-object"; import { assertNever } from "../common/helpers-pure"; import type { QueryHistoryInfo } from "./query-history-info"; -import { getLanguage } from "./query-history-info"; +import { getLanguage, getQueryId } from "./query-history-info"; import { QueryStatus } from "./query-status"; import type { HistoryItemLabelProvider } from "./history-item-label-provider"; import type { LanguageContextStore } from "../language-context-store"; @@ -17,6 +17,25 @@ export enum SortOrder { CountDesc = "CountDesc", } +/** + * Computes a stable, unique id for a query history item, suitable for use as a + * `TreeItem.id`. `getQueryId` alone is not unique because a multi-query run + * produces several local-query items that share the same `initialInfo.id`; for + * those we also include the (per-result) output base name. + */ +function getTreeItemId(element: QueryHistoryInfo): string { + switch (element.t) { + case "local": + return `local:${element.initialInfo.id}:${ + element.completedQuery?.query.outputBaseName ?? "" + }`; + case "variant-analysis": + return `variant-analysis:${getQueryId(element)}`; + default: + assertNever(element); + } +} + /** * Tree data provider for the query history view. */ @@ -54,6 +73,22 @@ export class HistoryTreeDataProvider async getTreeItem(element: QueryHistoryInfo): Promise { const treeItem = new TreeItem(this.labelProvider.getLabel(element)); + // Give the tree item a stable, unique id. Without this, VS Code identifies + // tree nodes by their label plus their position in the list. When multiple + // history items share the same label, that positional identity is ambiguous + // and makes `treeView.reveal` resolve to the wrong item (or fail outright) + // when the list is re-sorted or refreshed. The default label format includes + // the start time, so labels are effectively unique in practice and users are + // unlikely to hit this; it mainly affects tests (and any user who customises + // the label format to something non-unique). + // + // `getQueryId` is not guaranteed to be unique: a multi-query run produces + // several local-query items that share the same `initialInfo.id`. Those + // items differ by their output base name, so we include it to keep the id + // unique (VS Code de-duplicates nodes that share an id, which would break + // reveal/selection for all but one of them). + treeItem.id = getTreeItemId(element); + treeItem.command = { title: "Query History Item", command: "codeQLQueryHistory.itemClicked",