testing: allow toggling test output visibility

Also, just show outputs for the last test run.
This commit is contained in:
Connor Peet 2021-08-11 14:48:30 -07:00
parent 76cbce4663
commit 4b80b4cd36
No known key found for this signature in database
GPG key ID: CF8FD2EA0DBC61BD
5 changed files with 100 additions and 44 deletions

View file

@ -9,6 +9,7 @@ suite('Arrays', () => {
test('findFirst', () => { test('findFirst', () => {
const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69]; const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69];
console.log('hello');
let idx = arrays.findFirstInSorted(array, e => e >= 0); let idx = arrays.findFirstInSorted(array, e => e >= 0);
assert.strictEqual(array[idx], 1); assert.strictEqual(array[idx], 1);
@ -20,6 +21,7 @@ suite('Arrays', () => {
idx = arrays.findFirstInSorted(array, e => e >= 61); idx = arrays.findFirstInSorted(array, e => e >= 61);
assert.strictEqual(array[idx], 61); assert.strictEqual(array[idx], 61);
console.log('world');
idx = arrays.findFirstInSorted(array, e => e >= 69); idx = arrays.findFirstInSorted(array, e => e >= 69);
assert.strictEqual(array[idx], 69); assert.strictEqual(array[idx], 69);
@ -28,7 +30,7 @@ suite('Arrays', () => {
assert.strictEqual(idx, array.length); assert.strictEqual(idx, array.length);
idx = arrays.findFirstInSorted([], e => e >= 0); idx = arrays.findFirstInSorted([], e => e >= 0);
assert.strictEqual(array[idx], 1); assert.strictEqual(array[idx], 3);
}); });
test('quickSelect', () => { test('quickSelect', () => {

View file

@ -1103,6 +1103,30 @@ export class OpenOutputPeek extends Action2 {
} }
} }
export class ToggleInlineTestOutput extends Action2 {
public static readonly ID = 'testing.toggleInlineTestOutput';
constructor() {
super({
id: ToggleInlineTestOutput.ID,
title: localize('testing.toggleInlineTestOutput', "Toggle Inline Test Output"),
category,
keybinding: {
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.US_SEMICOLON, KeyMod.CtrlCmd | KeyCode.KEY_I),
},
menu: {
id: MenuId.CommandPalette,
when: TestingContextKeys.hasAnyResults.isEqualTo(true),
},
});
}
public async run(accessor: ServicesAccessor) {
const testService = accessor.get(ITestService);
testService.showInlineOutput.value = !testService.showInlineOutput.value;
}
}
export const allTestActions = [ export const allTestActions = [
// todo: these are disabled until we figure out how we want autorun to work // todo: these are disabled until we figure out how we want autorun to work
// AutoRunOffAction, // AutoRunOffAction,
@ -1136,5 +1160,6 @@ export const allTestActions = [
TestingSortByStatusAction, TestingSortByStatusAction,
TestingViewAsListAction, TestingViewAsListAction,
TestingViewAsTreeAction, TestingViewAsTreeAction,
ToggleInlineTestOutput,
UnhideTestAction, UnhideTestAction,
]; ];

View file

@ -38,6 +38,7 @@ import { IncrementalTestCollectionItem, InternalTestItem, IRichLocation, ITestMe
import { isFailedState, maxPriority } from 'vs/workbench/contrib/testing/common/testingStates'; import { isFailedState, maxPriority } from 'vs/workbench/contrib/testing/common/testingStates';
import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri';
import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService';
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
import { getContextForTestItem, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService'; import { getContextForTestItem, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService';
@ -126,17 +127,12 @@ export class TestingDecorations extends Disposable implements IEditorContributio
} }
})); }));
this._register(Event.any(this.results.onResultsChanged, this.testService.excluded.onTestExclusionsChanged)(() => { this._register(Event.any(
if (this.currentUri) { this.results.onResultsChanged,
this.setDecorations(this.currentUri); this.testService.excluded.onTestExclusionsChanged,
} this.testService.showInlineOutput.onDidChange,
})); this.testService.onDidProcessDiff,
)(() => this.setDecorations(this.currentUri)));
this._register(this.testService.onDidProcessDiff(() => {
if (this.currentUri) {
this.setDecorations(this.currentUri);
}
}));
} }
private attachModel(uri?: URI) { private attachModel(uri?: URI) {
@ -165,45 +161,56 @@ export class TestingDecorations extends Disposable implements IEditorContributio
this.setDecorations(uri); this.setDecorations(uri);
} }
private setDecorations(uri: URI): void { private setDecorations(uri: URI | undefined): void {
if (!uri) {
this.clearDecorations();
return;
}
this.editor.changeDecorations(accessor => { this.editor.changeDecorations(accessor => {
const newDecorations: ITestDecoration[] = []; const newDecorations: ITestDecoration[] = [];
for (const test of this.testService.collection.all) { for (const test of this.testService.collection.all) {
const stateLookup = this.results.getStateById(test.item.extId); if (!test.item.range || test.item.uri?.toString() !== uri.toString()) {
if (test.item.range && test.item.uri?.toString() === uri.toString()) {
const line = test.item.range.startLineNumber;
const resultItem = stateLookup?.[1];
const existing = newDecorations.findIndex(d => d instanceof RunTestDecoration && d.line === line);
if (existing !== -1) {
newDecorations[existing] = (newDecorations[existing] as RunTestDecoration).merge(test, resultItem);
} else {
newDecorations.push(this.instantiationService.createInstance(RunSingleTestDecoration, test, this.editor, stateLookup?.[1]));
}
}
if (!stateLookup) {
continue; continue;
} }
const [result, stateItem] = stateLookup; const stateLookup = this.results.getStateById(test.item.extId);
if (stateItem.retired) { const line = test.item.range.startLineNumber;
continue; // do not show decorations for outdated tests const resultItem = stateLookup?.[1];
const existing = newDecorations.findIndex(d => d instanceof RunTestDecoration && d.line === line);
if (existing !== -1) {
newDecorations[existing] = (newDecorations[existing] as RunTestDecoration).merge(test, resultItem);
} else {
newDecorations.push(this.instantiationService.createInstance(RunSingleTestDecoration, test, this.editor, stateLookup?.[1]));
}
}
const lastResult = this.results.results[0];
if (this.testService.showInlineOutput.value && lastResult instanceof LiveTestResult) {
for (const task of lastResult.tasks) {
for (const m of task.otherMessages) {
if (!this.invalidatedMessages.has(m) && hasValidLocation(uri, m)) {
newDecorations.push(this.instantiationService.createInstance(TestMessageDecoration, m, uri, m.location, this.editor));
}
}
} }
for (let taskId = 0; taskId < stateItem.tasks.length; taskId++) { for (const test of lastResult.tests) {
const state = stateItem.tasks[taskId]; for (let taskId = 0; taskId < test.tasks.length; taskId++) {
for (let i = 0; i < state.messages.length; i++) { const state = test.tasks[taskId];
const m = state.messages[i]; for (let i = 0; i < state.messages.length; i++) {
if (!this.invalidatedMessages.has(m) && hasValidLocation(uri, m)) { const m = state.messages[i];
const uri = m.type === TestMessageType.Info ? undefined : buildTestUri({ if (!this.invalidatedMessages.has(m) && hasValidLocation(uri, m)) {
type: TestUriType.ResultActualOutput, const uri = m.type === TestMessageType.Info ? undefined : buildTestUri({
messageIndex: i, type: TestUriType.ResultActualOutput,
taskIndex: taskId, messageIndex: i,
resultId: result.id, taskIndex: taskId,
testExtId: stateItem.item.extId, resultId: lastResult.id,
}); testExtId: test.item.extId,
});
newDecorations.push(this.instantiationService.createInstance(TestMessageDecoration, m, uri, m.location, this.editor)); newDecorations.push(this.instantiationService.createInstance(TestMessageDecoration, m, uri, m.location, this.editor));
}
} }
} }
} }
@ -218,6 +225,10 @@ export class TestingDecorations extends Disposable implements IEditorContributio
} }
private clearDecorations(): void { private clearDecorations(): void {
if (!this.lastDecorations.length) {
return;
}
this.editor.changeDecorations(accessor => { this.editor.changeDecorations(accessor => {
for (const decoration of this.lastDecorations) { for (const decoration of this.lastDecorations) {
accessor.removeDecoration(decoration.id); accessor.removeDecoration(decoration.id);

View file

@ -7,11 +7,11 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Event } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import * as extpath from 'vs/base/common/extpath'; import * as extpath from 'vs/base/common/extpath';
import { Iterable } from 'vs/base/common/iterator'; import { Iterable } from 'vs/base/common/iterator';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { MarshalledId } from 'vs/base/common/marshalling'; import { MarshalledId } from 'vs/base/common/marshalling';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { IObservableValue, MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue';
import { AbstractIncrementalTestCollection, IncrementalTestCollectionItem, InternalTestItem, ITestItemContext, ITestTagDisplayInfo, ResolvedTestRunRequest, RunTestForControllerRequest, TestItemExpandState, TestRunProfileBitset, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { AbstractIncrementalTestCollection, IncrementalTestCollectionItem, InternalTestItem, ITestItemContext, ITestTagDisplayInfo, ResolvedTestRunRequest, RunTestForControllerRequest, TestItemExpandState, TestRunProfileBitset, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { TestExclusions } from 'vs/workbench/contrib/testing/common/testExclusions'; import { TestExclusions } from 'vs/workbench/contrib/testing/common/testExclusions';
import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestId } from 'vs/workbench/contrib/testing/common/testId';
@ -225,6 +225,11 @@ export interface ITestService {
*/ */
readonly onDidProcessDiff: Event<TestsDiff>; readonly onDidProcessDiff: Event<TestsDiff>;
/**
* Whether inline editor decorations should be visible.
*/
readonly showInlineOutput: MutableObservableValue<boolean>;
/** /**
* Registers an interface that runs tests for the given provider ID. * Registers an interface that runs tests for the given provider ID.
*/ */

View file

@ -11,8 +11,11 @@ import { localize } from 'vs/nls';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { MainThreadTestCollection } from 'vs/workbench/contrib/testing/common/mainThreadTestCollection'; import { MainThreadTestCollection } from 'vs/workbench/contrib/testing/common/mainThreadTestCollection';
import { MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue';
import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue';
import { ResolvedTestRunRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { ResolvedTestRunRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { TestExclusions } from 'vs/workbench/contrib/testing/common/testExclusions'; import { TestExclusions } from 'vs/workbench/contrib/testing/common/testExclusions';
import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestId } from 'vs/workbench/contrib/testing/common/testId';
@ -55,9 +58,19 @@ export class TestService extends Disposable implements ITestService {
*/ */
public readonly excluded: TestExclusions; public readonly excluded: TestExclusions;
/**
* @inheritdoc
*/
public readonly showInlineOutput = MutableObservableValue.stored(new StoredValue<boolean>({
key: 'inlineTestOutputVisible',
scope: StorageScope.WORKSPACE,
target: StorageTarget.USER
}, this.storage), true);
constructor( constructor(
@IContextKeyService contextKeyService: IContextKeyService, @IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService, @IInstantiationService instantiationService: IInstantiationService,
@IStorageService private readonly storage: IStorageService,
@ITestProfileService private readonly testProfiles: ITestProfileService, @ITestProfileService private readonly testProfiles: ITestProfileService,
@INotificationService private readonly notificationService: INotificationService, @INotificationService private readonly notificationService: INotificationService,
@ITestResultService private readonly testResults: ITestResultService, @ITestResultService private readonly testResults: ITestResultService,