extension/src: add integration test for package symbols outline Change-Id: I96b93f48a2ee625597ff906e1eab6d39021b53fa Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/653035 Reviewed-by: Hongxiang Jiang <hxjiang@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> kokoro-CI: kokoro <noreply+kokoro@google.com>
diff --git a/extension/src/goPackageOutline.ts b/extension/src/goPackageOutline.ts index 486f61f..ce57fdf 100644 --- a/extension/src/goPackageOutline.ts +++ b/extension/src/goPackageOutline.ts
@@ -167,7 +167,7 @@ file: number; } -class PackageSymbol extends vscode.TreeItem { +export class PackageSymbol extends vscode.TreeItem { constructor( private readonly data: PackageSymbolData, private readonly files: string[],
diff --git a/extension/test/integration/goPackageOutline.test.ts b/extension/test/integration/goPackageOutline.test.ts new file mode 100644 index 0000000..e81dba7 --- /dev/null +++ b/extension/test/integration/goPackageOutline.test.ts
@@ -0,0 +1,110 @@ +/*--------------------------------------------------------- + * Copyright 2025 The Go Authors. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------*/ + +import assert from 'assert'; +import path from 'path'; +import { MockExtensionContext } from '../mocks/MockContext'; +import { GoPackageOutlineProvider, PackageSymbol } from '../../src/goPackageOutline'; +import { updateGoVarsFromConfig } from '../../src/goInstallTools'; +import { window } from 'vscode'; +import { Env } from '../gopls/goplsTestEnv.utils'; + +import { getGoConfig } from '../../src/config'; + +import vscode = require('vscode'); + +suite('GoPackageOutlineProvider', function () { + this.timeout(20000); + let provider: GoPackageOutlineProvider; + const fixtureDir = path.join(__dirname, '../../../test/testdata/packageOutlineTest'); + const ctx = MockExtensionContext.new(); + const env = new Env(); + + suiteSetup(async () => { + await updateGoVarsFromConfig({}); + await env.startGopls(undefined, getGoConfig(), fixtureDir); + provider = GoPackageOutlineProvider.setup(ctx); + }); + + suiteTeardown(() => { + ctx.teardown(); + }); + + test('opening a document should trigger package outline response', async () => { + const document = await vscode.workspace.openTextDocument( + vscode.Uri.file(path.join(fixtureDir, 'symbols_1.go')) + ); + await window.showTextDocument(document); + await sleep(500); // wait for gopls response + const res = provider.result; + assert.strictEqual(res?.PackageName, 'package_outline_test'); + assert.strictEqual(res?.Files.length, 2); + assert.strictEqual(res?.Symbols.length, 3); + assert.strictEqual(res?.Symbols[0].name, 'TestReceiver'); + assert.strictEqual(res?.Symbols[0].children.length, 6); // 3 fields and 3 receiver methods + }); + + test('clicking on symbol should navigate to definition', async () => { + const document = await vscode.workspace.openTextDocument( + vscode.Uri.file(path.join(fixtureDir, 'symbols_1.go')) + ); + await window.showTextDocument(document); + await sleep(500); // wait for gopls response + await vscode.commands.executeCommand('setContext', 'go.showPackageOutline'); + const children = await provider.getChildren(); + const receiver = children?.find((symbol) => symbol.label === 'TestReceiver'); + assert.ok(receiver, 'receiver symbol not found'); + const method1 = receiver.children?.find((symbol) => symbol.label === 'method1'); + assert.ok(method1, 'method1 symbol not found'); + clickSymbol(method1); + await sleep(500); // wait for editor to navigate to symbol + assert.strictEqual(window.activeTextEditor?.document.uri.fsPath, document.uri.fsPath); + assert.strictEqual(window.activeTextEditor?.selection.active.line, 19); + assert.strictEqual(window.activeTextEditor?.selection.active.character, 0); + }); + + test('clicking on symbol in different file should open file', async () => { + const document = await vscode.workspace.openTextDocument( + vscode.Uri.file(path.join(fixtureDir, 'symbols_1.go')) + ); + await window.showTextDocument(document); + await sleep(500); // wait for gopls response + await vscode.commands.executeCommand('setContext', 'go.showPackageOutline'); + const children = await provider.getChildren(); + const receiver = children?.find((symbol) => symbol.label === 'TestReceiver'); + assert.ok(receiver, 'receiver symbol not found'); + const method2 = receiver.children?.find((symbol) => symbol.label === 'method2'); + assert.ok(method2, 'method2 symbol not found'); + clickSymbol(method2); + await sleep(500); // wait for editor to navigate to symbol + const symbols2 = vscode.workspace.textDocuments.find( + (doc) => doc.uri.fsPath === path.join(fixtureDir, 'symbols_2.go') + ); + assert.strictEqual(window.activeTextEditor?.document.uri.fsPath, symbols2?.uri.fsPath); + assert.strictEqual(window.activeTextEditor?.selection.active.line, 2); + assert.strictEqual(window.activeTextEditor?.selection.active.character, 0); + }); + + test('non-go file does not trigger outline', async () => { + const document = await vscode.workspace.openTextDocument( + vscode.Uri.file(path.join(fixtureDir, 'symbols_3.ts')) + ); + await window.showTextDocument(document); + await sleep(500); // wait for gopls response + assert.strictEqual(provider.result, undefined); + }); +}); + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function clickSymbol(symbol: PackageSymbol) { + if (symbol.command) { + vscode.commands.executeCommand(symbol.command.command, ...(symbol.command.arguments || [])); + } else { + assert.fail(symbol.label + ' symbol has no command'); + } +}
diff --git a/extension/test/testdata/packageOutlineTest/symbols_1.go b/extension/test/testdata/packageOutlineTest/symbols_1.go new file mode 100644 index 0000000..9bf42f8 --- /dev/null +++ b/extension/test/testdata/packageOutlineTest/symbols_1.go
@@ -0,0 +1,22 @@ +package package_outline_test + +import ( + "fmt" +) + +func print(txt string) { + fmt.Println(txt) +} +func main() { + print("Hello") +} + +type TestReceiver struct { + field1 int + field2 string + field3 bool +} + +func (*TestReceiver) method1() { + +}
diff --git a/extension/test/testdata/packageOutlineTest/symbols_2.go b/extension/test/testdata/packageOutlineTest/symbols_2.go new file mode 100644 index 0000000..8ce1cb4 --- /dev/null +++ b/extension/test/testdata/packageOutlineTest/symbols_2.go
@@ -0,0 +1,9 @@ +package package_outline_test + +func (*TestReceiver) method2() { + +} + +func (*TestReceiver) method3() { + +}
diff --git a/extension/test/testdata/packageOutlineTest/symbols_3.ts b/extension/test/testdata/packageOutlineTest/symbols_3.ts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/extension/test/testdata/packageOutlineTest/symbols_3.ts