From 91c58f3618bc2e7d76318819c1f835e2c975a91c Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 16 May 2023 12:32:29 -0700 Subject: [PATCH 1/7] Update changelog For #2422. --- extensions/ql-vscode/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 767313d68..58e63b143 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -3,6 +3,7 @@ ## [UNRELEASED] - Add settings `codeQL.variantAnalysis.defaultResultsFilter` and `codeQL.variantAnalysis.defaultResultsSort` for configuring how variant analysis results are filtered and sorted in the results view. The default is to show all repositories, and to sort by the number of results. [#2392](https://github.com/github/vscode-codeql/pull/2392) +- Fix bug where the `CodeQL: Compare Query` command did not work for comparing quick-eval queries. [#2422](https://github.com/github/vscode-codeql/pull/2422) ## 1.8.4 - 3 May 2023 From 276e2cfdcf27d1bc99fc838a47ead0ffe7a16800 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 16 May 2023 14:48:19 -0700 Subject: [PATCH 2/7] Ensure full stack traces are included in log messages Pass in the `fullMessage` to `internalShowAndLog` so that stack traces aren't truncated. --- extensions/ql-vscode/src/common/vscode/commands.ts | 2 +- extensions/ql-vscode/src/helpers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/common/vscode/commands.ts b/extensions/ql-vscode/src/common/vscode/commands.ts index dfd765e17..27f8455e5 100644 --- a/extensions/ql-vscode/src/common/vscode/commands.ts +++ b/extensions/ql-vscode/src/common/vscode/commands.ts @@ -49,7 +49,6 @@ export function registerCommandWithErrorHandling( const errorMessage = redactableError(error)`${ getErrorMessage(e) || e } (${commandId})`; - const errorStack = getErrorStack(e); if (e instanceof UserCancellationException) { // User has cancelled this action manually if (e.silent) { @@ -61,6 +60,7 @@ export function registerCommandWithErrorHandling( } } else { // Include the full stack in the error log only. + const errorStack = getErrorStack(e); const fullMessage = errorStack ? `${errorMessage.fullMessage}\n${errorStack}` : errorMessage.fullMessage; diff --git a/extensions/ql-vscode/src/helpers.ts b/extensions/ql-vscode/src/helpers.ts index e7e33f206..d4e8a9a8c 100644 --- a/extensions/ql-vscode/src/helpers.ts +++ b/extensions/ql-vscode/src/helpers.ts @@ -98,7 +98,7 @@ export async function showAndLogErrorMessage( return internalShowAndLog( dropLinesExceptInitial(message), Window.showErrorMessage, - options, + { fullMessage: message, ...options }, ); } From 55b65e33ad2a997d8c63fd2691138657bcee8027 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 16 May 2023 14:51:03 -0700 Subject: [PATCH 3/7] Update changelog --- extensions/ql-vscode/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 767313d68..1d07f268c 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -3,6 +3,7 @@ ## [UNRELEASED] - Add settings `codeQL.variantAnalysis.defaultResultsFilter` and `codeQL.variantAnalysis.defaultResultsSort` for configuring how variant analysis results are filtered and sorted in the results view. The default is to show all repositories, and to sort by the number of results. [#2392](https://github.com/github/vscode-codeql/pull/2392) +- Fix bug to ensure error messages have complete stack trace in message logs. [#2425](https://github.com/github/vscode-codeql/pull/2425) ## 1.8.4 - 3 May 2023 From ec1fda21d031b6ec2babc3915c38ad2629366b66 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 16 May 2023 17:25:01 +0100 Subject: [PATCH 4/7] Open query file on click --- .../queries-panel/query-tree-data-provider.ts | 17 ++++++++++++----- .../src/queries-panel/query-tree-view-item.ts | 16 +++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/src/queries-panel/query-tree-data-provider.ts b/extensions/ql-vscode/src/queries-panel/query-tree-data-provider.ts index ae9492fee..85c446465 100644 --- a/extensions/ql-vscode/src/queries-panel/query-tree-data-provider.ts +++ b/extensions/ql-vscode/src/queries-panel/query-tree-data-provider.ts @@ -17,11 +17,18 @@ export class QueryTreeDataProvider private createTree(): QueryTreeViewItem[] { // Temporary mock data, just to populate the tree view. return [ - new QueryTreeViewItem("name1", "path1", []), - new QueryTreeViewItem("name2", "path2", [ - new QueryTreeViewItem("name3", "path3", []), - new QueryTreeViewItem("name4", "path4", [ - new QueryTreeViewItem("name5", "path5", []), + new QueryTreeViewItem("custom-pack", [ + new QueryTreeViewItem("custom-pack/example.ql", []), + ]), + new QueryTreeViewItem("ql", [ + new QueryTreeViewItem("ql/javascript", [ + new QueryTreeViewItem("ql/javascript/example.ql", []), + ]), + new QueryTreeViewItem("ql/go", [ + new QueryTreeViewItem("ql/go/security", [ + new QueryTreeViewItem("ql/go/security/query1.ql", []), + new QueryTreeViewItem("ql/go/security/query2.ql", []), + ]), ]), ]), ]; diff --git a/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts b/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts index 0efb36692..a6f82030a 100644 --- a/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts +++ b/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts @@ -1,15 +1,17 @@ import * as vscode from "vscode"; +import { basename } from "path"; export class QueryTreeViewItem extends vscode.TreeItem { - constructor( - label: string, - tooltip: string | undefined, - public readonly children: QueryTreeViewItem[], - ) { - super(label); - this.tooltip = tooltip; + constructor(path: string, public readonly children: QueryTreeViewItem[]) { + super(basename(path)); + this.tooltip = path; this.collapsibleState = this.children.length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None; + this.command = { + title: "Open", + command: "vscode.open", + arguments: [vscode.Uri.file(path)], + }; } } From 565a7a53e012e7466f2a7ce8b885ecdc91f0ea16 Mon Sep 17 00:00:00 2001 From: shati-patel <42641846+shati-patel@users.noreply.github.com> Date: Wed, 17 May 2023 14:43:43 +0100 Subject: [PATCH 5/7] Only implement "open file" when you click a file node (not a folder) --- .../src/queries-panel/query-tree-view-item.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts b/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts index a6f82030a..f9adc5c00 100644 --- a/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts +++ b/extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts @@ -8,10 +8,12 @@ export class QueryTreeViewItem extends vscode.TreeItem { this.collapsibleState = this.children.length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None; - this.command = { - title: "Open", - command: "vscode.open", - arguments: [vscode.Uri.file(path)], - }; + if (this.children.length === 0) { + this.command = { + title: "Open", + command: "vscode.open", + arguments: [vscode.Uri.file(path)], + }; + } } } From a5af2f2e4bf44d89cae9b4bfe0da4a14b59d5c1e Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 19 May 2023 12:16:48 +0100 Subject: [PATCH 6/7] Pull out node classes to shared location --- .../ql-vscode/src/common/file-tree-nodes.ts | 103 +++++++++++++++ .../src/query-testing/qltest-discovery.ts | 119 ++---------------- .../src/query-testing/test-adapter.ts | 24 ++-- .../src/query-testing/test-manager.ts | 20 +-- 4 files changed, 135 insertions(+), 131 deletions(-) create mode 100644 extensions/ql-vscode/src/common/file-tree-nodes.ts diff --git a/extensions/ql-vscode/src/common/file-tree-nodes.ts b/extensions/ql-vscode/src/common/file-tree-nodes.ts new file mode 100644 index 000000000..32144c885 --- /dev/null +++ b/extensions/ql-vscode/src/common/file-tree-nodes.ts @@ -0,0 +1,103 @@ +import { basename, dirname, join } from "path"; +import { env } from "vscode"; + +/** + * A node in the tree of files. This will be either a `FileTreeDirectory` or a `FileTreeLeaf`. + */ +export abstract class FileTreeNode { + constructor(private _path: string, private _name: string) {} + + public get path(): string { + return this._path; + } + + public get name(): string { + return this._name; + } + + public abstract get children(): readonly FileTreeNode[]; + + public abstract finish(): void; +} + +/** + * A directory containing one or more files or other directories. + */ +export class FileTreeDirectory extends FileTreeNode { + constructor( + _path: string, + _name: string, + private _children: FileTreeNode[] = [], + ) { + super(_path, _name); + } + + public get children(): readonly FileTreeNode[] { + return this._children; + } + + public addChild(child: FileTreeNode): void { + this._children.push(child); + } + + public createDirectory(relativePath: string): FileTreeDirectory { + const dirName = dirname(relativePath); + if (dirName === ".") { + return this.createChildDirectory(relativePath); + } else { + const parent = this.createDirectory(dirName); + return parent.createDirectory(basename(relativePath)); + } + } + + public finish(): void { + // remove empty directories + this._children.filter( + (child) => child instanceof FileTreeLeaf || child.children.length > 0, + ); + this._children.sort((a, b) => a.name.localeCompare(b.name, env.language)); + this._children.forEach((child, i) => { + child.finish(); + if ( + child.children?.length === 1 && + child.children[0] instanceof FileTreeDirectory + ) { + // collapse children + const replacement = new FileTreeDirectory( + child.children[0].path, + `${child.name} / ${child.children[0].name}`, + Array.from(child.children[0].children), + ); + this._children[i] = replacement; + } + }); + } + + private createChildDirectory(name: string): FileTreeDirectory { + const existingChild = this._children.find((child) => child.name === name); + if (existingChild !== undefined) { + return existingChild as FileTreeDirectory; + } else { + const newChild = new FileTreeDirectory(join(this.path, name), name); + this.addChild(newChild); + return newChild; + } + } +} + +/** + * A single file. + */ +export class FileTreeLeaf extends FileTreeNode { + constructor(_path: string, _name: string) { + super(_path, _name); + } + + public get children(): readonly FileTreeNode[] { + return []; + } + + public finish(): void { + /**/ + } +} diff --git a/extensions/ql-vscode/src/query-testing/qltest-discovery.ts b/extensions/ql-vscode/src/query-testing/qltest-discovery.ts index 891d2c965..b7d333ff6 100644 --- a/extensions/ql-vscode/src/query-testing/qltest-discovery.ts +++ b/extensions/ql-vscode/src/query-testing/qltest-discovery.ts @@ -1,4 +1,4 @@ -import { dirname, basename, join, normalize, relative, extname } from "path"; +import { dirname, basename, normalize, relative, extname } from "path"; import { Discovery } from "../common/discovery"; import { EventEmitter, @@ -6,112 +6,11 @@ import { Uri, RelativePattern, WorkspaceFolder, - env, } from "vscode"; import { MultiFileSystemWatcher } from "../common/vscode/multi-file-system-watcher"; import { CodeQLCliServer } from "../codeql-cli/cli"; import { pathExists } from "fs-extra"; - -/** - * A node in the tree of tests. This will be either a `QLTestDirectory` or a `QLTestFile`. - */ -export abstract class QLTestNode { - constructor(private _path: string, private _name: string) {} - - public get path(): string { - return this._path; - } - - public get name(): string { - return this._name; - } - - public abstract get children(): readonly QLTestNode[]; - - public abstract finish(): void; -} - -/** - * A directory containing one or more QL tests or other test directories. - */ -export class QLTestDirectory extends QLTestNode { - constructor( - _path: string, - _name: string, - private _children: QLTestNode[] = [], - ) { - super(_path, _name); - } - - public get children(): readonly QLTestNode[] { - return this._children; - } - - public addChild(child: QLTestNode): void { - this._children.push(child); - } - - public createDirectory(relativePath: string): QLTestDirectory { - const dirName = dirname(relativePath); - if (dirName === ".") { - return this.createChildDirectory(relativePath); - } else { - const parent = this.createDirectory(dirName); - return parent.createDirectory(basename(relativePath)); - } - } - - public finish(): void { - // remove empty directories - this._children.filter( - (child) => child instanceof QLTestFile || child.children.length > 0, - ); - this._children.sort((a, b) => a.name.localeCompare(b.name, env.language)); - this._children.forEach((child, i) => { - child.finish(); - if ( - child.children?.length === 1 && - child.children[0] instanceof QLTestDirectory - ) { - // collapse children - const replacement = new QLTestDirectory( - child.children[0].path, - `${child.name} / ${child.children[0].name}`, - Array.from(child.children[0].children), - ); - this._children[i] = replacement; - } - }); - } - - private createChildDirectory(name: string): QLTestDirectory { - const existingChild = this._children.find((child) => child.name === name); - if (existingChild !== undefined) { - return existingChild as QLTestDirectory; - } else { - const newChild = new QLTestDirectory(join(this.path, name), name); - this.addChild(newChild); - return newChild; - } - } -} - -/** - * A single QL test. This will be either a `.ql` file or a `.qlref` file. - */ -export class QLTestFile extends QLTestNode { - constructor(_path: string, _name: string) { - super(_path, _name); - } - - public get children(): readonly QLTestNode[] { - return []; - } - - public finish(): void { - /**/ - } -} +import { FileTreeDirectory, FileTreeLeaf } from "../common/file-tree-nodes"; /** * The results of discovering QL tests. @@ -120,7 +19,7 @@ interface QLTestDiscoveryResults { /** * A directory that contains one or more QL Tests, or other QLTestDirectories. */ - testDirectory: QLTestDirectory | undefined; + testDirectory: FileTreeDirectory | undefined; /** * The file system path to a directory to watch. If any ql or qlref file changes in @@ -137,7 +36,7 @@ export class QLTestDiscovery extends Discovery { private readonly watcher: MultiFileSystemWatcher = this.push( new MultiFileSystemWatcher(), ); - private _testDirectory: QLTestDirectory | undefined; + private _testDirectory: FileTreeDirectory | undefined; constructor( private readonly workspaceFolder: WorkspaceFolder, @@ -159,7 +58,7 @@ export class QLTestDiscovery extends Discovery { * The root directory. There is at least one test in this directory, or * in a subdirectory of this. */ - public get testDirectory(): QLTestDirectory | undefined { + public get testDirectory(): FileTreeDirectory | undefined { return this._testDirectory; } @@ -194,10 +93,10 @@ export class QLTestDiscovery extends Discovery { * @returns A `QLTestDirectory` object describing the contents of the directory, or `undefined` if * no tests were found. */ - private async discoverTests(): Promise { + private async discoverTests(): Promise { const fullPath = this.workspaceFolder.uri.fsPath; const name = this.workspaceFolder.name; - const rootDirectory = new QLTestDirectory(fullPath, name); + const rootDirectory = new FileTreeDirectory(fullPath, name); // Don't try discovery on workspace folders that don't exist on the filesystem if (await pathExists(fullPath)) { @@ -208,7 +107,9 @@ export class QLTestDiscovery extends Discovery { const relativePath = normalize(relative(fullPath, testPath)); const dirName = dirname(relativePath); const parentDirectory = rootDirectory.createDirectory(dirName); - parentDirectory.addChild(new QLTestFile(testPath, basename(testPath))); + parentDirectory.addChild( + new FileTreeLeaf(testPath, basename(testPath)), + ); } rootDirectory.finish(); diff --git a/extensions/ql-vscode/src/query-testing/test-adapter.ts b/extensions/ql-vscode/src/query-testing/test-adapter.ts index dae8f9780..86c37b845 100644 --- a/extensions/ql-vscode/src/query-testing/test-adapter.ts +++ b/extensions/ql-vscode/src/query-testing/test-adapter.ts @@ -13,17 +13,17 @@ import { TestHub, } from "vscode-test-adapter-api"; import { TestAdapterRegistrar } from "vscode-test-adapter-util"; -import { - QLTestFile, - QLTestNode, - QLTestDirectory, - QLTestDiscovery, -} from "./qltest-discovery"; +import { QLTestDiscovery } from "./qltest-discovery"; import { Event, EventEmitter, CancellationTokenSource } from "vscode"; import { DisposableObject } from "../pure/disposable-object"; import { CodeQLCliServer, TestCompleted } from "../codeql-cli/cli"; import { testLogger } from "../common"; import { TestRunner } from "./test-runner"; +import { + FileTreeDirectory, + FileTreeLeaf, + FileTreeNode, +} from "../common/file-tree-nodes"; /** * Get the full path of the `.expected` file for the specified QL test. @@ -135,7 +135,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter { } private static createTestOrSuiteInfos( - testNodes: readonly QLTestNode[], + testNodes: readonly FileTreeNode[], ): Array { return testNodes.map((childNode) => { return QLTestAdapter.createTestOrSuiteInfo(childNode); @@ -143,18 +143,18 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter { } private static createTestOrSuiteInfo( - testNode: QLTestNode, + testNode: FileTreeNode, ): TestSuiteInfo | TestInfo { - if (testNode instanceof QLTestFile) { + if (testNode instanceof FileTreeLeaf) { return QLTestAdapter.createTestInfo(testNode); - } else if (testNode instanceof QLTestDirectory) { + } else if (testNode instanceof FileTreeDirectory) { return QLTestAdapter.createTestSuiteInfo(testNode, testNode.name); } else { throw new Error("Unexpected test type."); } } - private static createTestInfo(testFile: QLTestFile): TestInfo { + private static createTestInfo(testFile: FileTreeLeaf): TestInfo { return { type: "test", id: testFile.path, @@ -165,7 +165,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter { } private static createTestSuiteInfo( - testDirectory: QLTestDirectory, + testDirectory: FileTreeDirectory, label: string, ): TestSuiteInfo { return { diff --git a/extensions/ql-vscode/src/query-testing/test-manager.ts b/extensions/ql-vscode/src/query-testing/test-manager.ts index fc5789b8e..94b79432b 100644 --- a/extensions/ql-vscode/src/query-testing/test-manager.ts +++ b/extensions/ql-vscode/src/query-testing/test-manager.ts @@ -16,12 +16,7 @@ import { workspace, } from "vscode"; import { DisposableObject } from "../pure/disposable-object"; -import { - QLTestDirectory, - QLTestDiscovery, - QLTestFile, - QLTestNode, -} from "./qltest-discovery"; +import { QLTestDiscovery } from "./qltest-discovery"; import { CodeQLCliServer } from "../codeql-cli/cli"; import { getErrorMessage } from "../pure/helpers-pure"; import { BaseLogger, LogOptions } from "../common"; @@ -29,6 +24,11 @@ import { TestRunner } from "./test-runner"; import { TestManagerBase } from "./test-manager-base"; import { App } from "../common/app"; import { isWorkspaceFolderOnDisk } from "../helpers"; +import { + FileTreeDirectory, + FileTreeLeaf, + FileTreeNode, +} from "../common/file-tree-nodes"; /** * Returns the complete text content of the specified file. If there is an error reading the file, @@ -209,7 +209,7 @@ export class TestManager extends TestManagerBase { */ public updateTestsForWorkspaceFolder( workspaceFolder: WorkspaceFolder, - testDirectory: QLTestDirectory | undefined, + testDirectory: FileTreeDirectory | undefined, ): void { if (testDirectory !== undefined) { // Adding an item with the same ID as an existing item will replace it, which is exactly what @@ -229,9 +229,9 @@ export class TestManager extends TestManagerBase { /** * Creates a tree of `TestItem`s from the root `QlTestNode` provided by test discovery. */ - private createTestItemTree(node: QLTestNode, isRoot: boolean): TestItem { + private createTestItemTree(node: FileTreeNode, isRoot: boolean): TestItem { // Prefix the ID to identify it as a directory or a test - const itemType = node instanceof QLTestDirectory ? "dir" : "test"; + const itemType = node instanceof FileTreeDirectory ? "dir" : "test"; const testItem = this.testController.createTestItem( // For the root of a workspace folder, use the full path as the ID. Otherwise, use the node's // name as the ID, since it's shorter but still unique. @@ -242,7 +242,7 @@ export class TestManager extends TestManagerBase { for (const childNode of node.children) { const childItem = this.createTestItemTree(childNode, false); - if (childNode instanceof QLTestFile) { + if (childNode instanceof FileTreeLeaf) { childItem.range = new Range(0, 0, 0, 0); } testItem.children.add(childItem); From a7f6401be7ed85146a0955a1582790955f5b3dad Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 19 May 2023 12:17:34 +0100 Subject: [PATCH 7/7] Add workspace folders to App interface --- extensions/ql-vscode/src/common/app.ts | 7 +++++++ extensions/ql-vscode/src/common/vscode/vscode-app.ts | 8 ++++++++ extensions/ql-vscode/test/__mocks__/appMock.ts | 11 +++++++++++ 3 files changed, 26 insertions(+) diff --git a/extensions/ql-vscode/src/common/app.ts b/extensions/ql-vscode/src/common/app.ts index 2e749c018..f50eb729d 100644 --- a/extensions/ql-vscode/src/common/app.ts +++ b/extensions/ql-vscode/src/common/app.ts @@ -4,6 +4,11 @@ import { AppEventEmitter } from "./events"; import { Logger } from "./logging"; import { Memento } from "./memento"; import { AppCommandManager } from "./commands"; +import type { + WorkspaceFolder, + Event, + WorkspaceFoldersChangeEvent, +} from "vscode"; export interface App { createEventEmitter(): AppEventEmitter; @@ -14,6 +19,8 @@ export interface App { readonly globalStoragePath: string; readonly workspaceStoragePath?: string; readonly workspaceState: Memento; + readonly workspaceFolders: readonly WorkspaceFolder[] | undefined; + readonly onDidChangeWorkspaceFolders: Event; readonly credentials: Credentials; readonly commands: AppCommandManager; } diff --git a/extensions/ql-vscode/src/common/vscode/vscode-app.ts b/extensions/ql-vscode/src/common/vscode/vscode-app.ts index 986d74c9a..9bc12afef 100644 --- a/extensions/ql-vscode/src/common/vscode/vscode-app.ts +++ b/extensions/ql-vscode/src/common/vscode/vscode-app.ts @@ -39,6 +39,14 @@ export class ExtensionApp implements App { return this.extensionContext.workspaceState; } + public get workspaceFolders(): readonly vscode.WorkspaceFolder[] | undefined { + return vscode.workspace.workspaceFolders; + } + + public get onDidChangeWorkspaceFolders(): vscode.Event { + return vscode.workspace.onDidChangeWorkspaceFolders; + } + public get subscriptions(): Disposable[] { return this.extensionContext.subscriptions; } diff --git a/extensions/ql-vscode/test/__mocks__/appMock.ts b/extensions/ql-vscode/test/__mocks__/appMock.ts index 1b6d9da9d..d53fa58b9 100644 --- a/extensions/ql-vscode/test/__mocks__/appMock.ts +++ b/extensions/ql-vscode/test/__mocks__/appMock.ts @@ -8,6 +8,11 @@ import { testCredentialsWithStub } from "../factories/authentication"; import { Credentials } from "../../src/common/authentication"; import { AppCommandManager } from "../../src/common/commands"; import { createMockCommandManager } from "./commandsMock"; +import type { + Event, + WorkspaceFolder, + WorkspaceFoldersChangeEvent, +} from "vscode"; export function createMockApp({ extensionPath = "/mock/extension/path", @@ -15,6 +20,8 @@ export function createMockApp({ globalStoragePath = "/mock/global/storage/path", createEventEmitter = () => new MockAppEventEmitter(), workspaceState = createMockMemento(), + workspaceFolders = [], + onDidChangeWorkspaceFolders = jest.fn(), credentials = testCredentialsWithStub(), commands = createMockCommandManager(), }: { @@ -23,6 +30,8 @@ export function createMockApp({ globalStoragePath?: string; createEventEmitter?: () => AppEventEmitter; workspaceState?: Memento; + workspaceFolders?: readonly WorkspaceFolder[] | undefined; + onDidChangeWorkspaceFolders?: Event; credentials?: Credentials; commands?: AppCommandManager; }): App { @@ -34,6 +43,8 @@ export function createMockApp({ workspaceStoragePath, globalStoragePath, workspaceState, + workspaceFolders, + onDidChangeWorkspaceFolders, createEventEmitter, credentials, commands,