From 6763d4ff5e0a5d542af0fdf123f36fdf98a2cba9 Mon Sep 17 00:00:00 2001 From: darylcogswell Date: Sun, 28 Feb 2021 10:09:45 -0500 Subject: [PATCH 1/4] Add Show Test Output feature --- .gitignore | 3 ++- .vscode/launch.json | 12 ++++++++++++ package.json | 15 ++++++++++++++- src/extension.ts | 7 +++++++ src/testCommands.ts | 16 ++++++++++++++++ src/testResult.ts | 10 +++++++++- src/testResultDocumentContentProvider.ts | 16 ++++++++++++++++ src/testResultsFile.ts | 1 + test/problems.test.ts | 2 +- test/testNode.test.ts | 2 +- 10 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 src/testResultDocumentContentProvider.ts diff --git a/.gitignore b/.gitignore index b631c59..7b40a66 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ npm-debug.log yarn.* [Bb]in/ [Oo]bj/ -lcov.info \ No newline at end of file +lcov.info +.vscode-test/** \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index d105ede..acd53a0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,6 +2,7 @@ { "version": "0.1.0", "configurations": [ + { "name": "Launch Extension (xunit)", "type": "extensionHost", @@ -118,6 +119,17 @@ "sourceMaps": true, "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "npm" + }, + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}", + ], + "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], + "preLaunchTask": "npm" } ] } diff --git a/package.json b/package.json index 9a19e81..ec021a8 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,14 @@ { "command": "dotnet-test-explorer.openPanel", "title": "Open Tests Panel" + }, + { + "command": "dotnet-test-explorer.showTestOutput", + "title": "Show Test Output", + "icon": { + "light": "resources/light/log.svg", + "dark": "resources/dark/log.svg" + } } ], "menus": { @@ -143,7 +151,7 @@ { "command": "dotnet-test-explorer.runTest", "when": "view == dotnetTestExplorer", - "group": "inline" + "group": "inline@0" }, { "command": "dotnet-test-explorer.gotoTest", @@ -154,6 +162,11 @@ "command": "dotnet-test-explorer.debugTest", "when": "viewItem == test", "group": "dotnetTestExplorer@2" + }, + { + "command": "dotnet-test-explorer.showTestOutput", + "when": "viewItem == test", + "group": "inline@1" } ], "editor/context": [ diff --git a/src/extension.ts b/src/extension.ts index 008d1a0..8b952a7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -17,6 +17,7 @@ import { TestNode } from "./testNode"; import { TestStatusCodeLensProvider } from "./testStatusCodeLensProvider"; import { Utility } from "./utility"; import { Watch } from "./watch"; +import { TestResultDocumentContentProvider } from './testResultDocumentContentProvider'; export function activate(context: vscode.ExtensionContext) { const testDirectories = new TestDirectories(); @@ -115,6 +116,12 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.window.onDidCloseTerminal((closedTerminal: vscode.Terminal) => { Executor.onDidCloseTerminal(closedTerminal); })); + + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider("dotnet-test-explorer.testResult", new TestResultDocumentContentProvider())); + + context.subscriptions.push(vscode.commands.registerCommand("dotnet-test-explorer.showTestOutput", (test: TestNode) => { + testCommands.showTestOutput(test); + })); } export function deactivate() { diff --git a/src/testCommands.ts b/src/testCommands.ts index 042dc43..aa5b20f 100644 --- a/src/testCommands.ts +++ b/src/testCommands.ts @@ -25,6 +25,7 @@ export class TestCommands implements Disposable { private onNewTestResultsEmitter = new EventEmitter(); private onBuildFailedEmitter = new EventEmitter(); private lastRunTestContext: ITestRunContext = null; + private lastRunTestResults: TestResult[] = []; private testResultsFolder: string; private testResultsFolderWatcher: any; @@ -203,6 +204,7 @@ export class TestCommands implements Disposable { allTestResults.push(...testResults); } this.sendNewTestResults({ clearPreviousTestResults: testName === "", testResults: allTestResults }); + this.lastRunTestResults = allTestResults; } catch (err) { Logger.Log(`Error while executing test command: ${err}`); if (err.message === "Build command failed") { @@ -296,4 +298,18 @@ export class TestCommands implements Disposable { }); }); } + + public async showTestOutput(test: TestNode) { + const testResult = this.lastRunTestResults.find(x => x.name == test.name); + if (!testResult) vscode.window.showInformationMessage(`Result not found for test '${test.name}'. Make sure the test is included in a test run.`); + + const uri = vscode.Uri.parse(`dotnet-test-explorer.testResult:${testResult?.name}`) + .with({ + query: testResult.stdout, + fragment: testResult.outcome + }); + + const doc = await vscode.workspace.openTextDocument(uri); + await vscode.window.showTextDocument(doc, { preview: false }); + } } diff --git a/src/testResult.ts b/src/testResult.ts index 15e7826..5f3b4c1 100644 --- a/src/testResult.ts +++ b/src/testResult.ts @@ -7,7 +7,7 @@ export class TestResult { private className: string; private method: string; - public constructor(private _testId: string, private _outcome: string, private _message: string, private _stackTrace: string) { + public constructor(private _testId: string, private _outcome: string, private _message: string, private _stackTrace: string, private _stdout: string) { } public get fullName(): string { @@ -30,6 +30,14 @@ export class TestResult { return this._stackTrace; } + public get stdout(): string { + return this._stdout; + } + + public get name(): string { + return this.method; + } + public matches(className: string, method: string): boolean { return this.fullName.indexOf(className + "." + method) > -1; } diff --git a/src/testResultDocumentContentProvider.ts b/src/testResultDocumentContentProvider.ts new file mode 100644 index 0000000..5d603c6 --- /dev/null +++ b/src/testResultDocumentContentProvider.ts @@ -0,0 +1,16 @@ +"use strict"; +import { TextDocumentContentProvider, Uri, EventEmitter } from 'vscode'; + +export class TestResultDocumentContentProvider implements TextDocumentContentProvider { + onDidChangeEmitter = new EventEmitter(); + onDidChange = this.onDidChangeEmitter.event; + + provideTextDocumentContent(uri: Uri): string { + return ` +Test Name: ${uri.path} +Test Outcome: ${uri.fragment} +Standard Output: +${uri.query} +`; + } +} \ No newline at end of file diff --git a/src/testResultsFile.ts b/src/testResultsFile.ts index ae4ed22..e63c73c 100644 --- a/src/testResultsFile.ts +++ b/src/testResultsFile.ts @@ -39,6 +39,7 @@ function parseUnitTestResults(xml: Element): TestResult[] { getAttributeValue(nodes[i], "outcome"), getTextContentForTag(nodes[i], "Message"), getTextContentForTag(nodes[i], "StackTrace"), + getTextContentForTag(nodes[i], "StdOut"), )); } diff --git a/test/problems.test.ts b/test/problems.test.ts index 54354a7..2ce57cc 100644 --- a/test/problems.test.ts +++ b/test/problems.test.ts @@ -64,5 +64,5 @@ suite("Problems tests", () => { }); function GetTestResult(id: string, outcome: string, message: string, stackTrace: string) { - return new TestResult(id, outcome, message, stackTrace); + return new TestResult(id, outcome, message, stackTrace, ""); } diff --git a/test/testNode.test.ts b/test/testNode.test.ts index cffe160..c87e0d0 100644 --- a/test/testNode.test.ts +++ b/test/testNode.test.ts @@ -92,7 +92,7 @@ suite("Icon tests", () => { }); function GetTestResult(id: string, outcome: string, className: string, method: string) { - const testResult = new TestResult(id, outcome, "", ""); + const testResult = new TestResult(id, outcome, "", "", ""); testResult.updateName(className, method); return testResult; } From ce2eef036227fea073767e62f77ca1139706f1d5 Mon Sep 17 00:00:00 2001 From: Daryl Date: Mon, 1 Mar 2021 13:34:27 -0500 Subject: [PATCH 2/4] Add script to restore dotnet packages --- package.json | 3 ++- test/restorePackages.js | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 test/restorePackages.js diff --git a/package.json b/package.json index ec021a8..edc0c4d 100644 --- a/package.json +++ b/package.json @@ -307,7 +307,8 @@ "compile": "tsc -p ./", "watch": "tsc -watch -p ./", "test": "node ./out/test/runTest.js", - "tslint": "tslint -t verbose src/**/*.ts" + "tslint": "tslint -t verbose src/**/*.ts", + "precompile": "node ./test/restorePackages.js" }, "devDependencies": { "@types/glob": "^7.1.1", diff --git a/test/restorePackages.js b/test/restorePackages.js new file mode 100644 index 0000000..a116ff0 --- /dev/null +++ b/test/restorePackages.js @@ -0,0 +1,8 @@ +let { exec } = require('child_process'); +let log = (err, stdout, stderr) => console.log(stdout) + +exec("dotnet restore ./test/fsxunittests/FSharpTests.fsproj", log); +exec("dotnet restore ./test/mstest/MSTestTests.csproj", log); +exec("dotnet restore ./test/nunit/NunitTests.csproj", log); +exec("dotnet restore ./test/nunitNet5/NunitTests.csproj", log); +exec("dotnet restore ./test/xunittests/XunitTests.csproj", log); \ No newline at end of file From fa0401678e64d18b7926aeb305a4133064b90f40 Mon Sep 17 00:00:00 2001 From: Daryl Date: Mon, 1 Mar 2021 18:21:15 -0500 Subject: [PATCH 3/4] Show exception message and stack trace when test errors --- src/testResult.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/testResult.ts b/src/testResult.ts index 5f3b4c1..d78aa7b 100644 --- a/src/testResult.ts +++ b/src/testResult.ts @@ -31,7 +31,8 @@ export class TestResult { } public get stdout(): string { - return this._stdout; + return this._stdout + || `${this._message}\r\n${this._stackTrace}` ; } public get name(): string { From 0d60f79345194327ca4031b891b824a358db75a7 Mon Sep 17 00:00:00 2001 From: Daryl Date: Tue, 2 Mar 2021 18:31:29 -0500 Subject: [PATCH 4/4] Set preview to true --- src/testCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testCommands.ts b/src/testCommands.ts index aa5b20f..44e8107 100644 --- a/src/testCommands.ts +++ b/src/testCommands.ts @@ -310,6 +310,6 @@ export class TestCommands implements Disposable { }); const doc = await vscode.workspace.openTextDocument(uri); - await vscode.window.showTextDocument(doc, { preview: false }); + await vscode.window.showTextDocument(doc, { preview: true }); } }