parent
480f3c0b35
commit
6014c7781e
5 changed files with 70 additions and 31 deletions
|
@ -34,11 +34,11 @@ import { TestingExplorerView, TestingExplorerViewModel } from 'vs/workbench/cont
|
|||
import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
|
||||
import { ITestingOutputTerminalService } from 'vs/workbench/contrib/testing/browser/testingOutputTerminalService';
|
||||
import { TestExplorerViewMode, TestExplorerViewSorting, Testing } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { InternalTestItem, ITestItem, TestIdPath, TestIdWithSrc, TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { InternalTestItem, ITestItem, TestIdPath, TestIdWithSrc } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ITestingAutoRun } from 'vs/workbench/contrib/testing/common/testingAutoRun';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { getPathForTestInResult, ITestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { getAllTestsInHierarchy, getTestByPath, ITestService, waitForAllRoots } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { IWorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
|
||||
|
@ -853,21 +853,6 @@ abstract class RunOrDebugExtsById extends Action2 {
|
|||
protected abstract filter(node: InternalTestItem): boolean;
|
||||
|
||||
protected abstract runTest(service: ITestService, node: InternalTestItem[]): Promise<ITestResult>;
|
||||
|
||||
protected getPathForTest(test: TestResultItem, results: ITestResult) {
|
||||
const path = [test];
|
||||
while (true) {
|
||||
const parentId = path[0].parent;
|
||||
const parent = parentId && results.getStateById(parentId);
|
||||
if (!parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
path.unshift(parent);
|
||||
}
|
||||
|
||||
return path.map(t => t.item.extId);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class RunOrDebugFailedTests extends RunOrDebugExtsById {
|
||||
|
@ -881,7 +866,7 @@ abstract class RunOrDebugFailedTests extends RunOrDebugExtsById {
|
|||
for (let i = results.length - 1; i >= 0; i--) {
|
||||
const resultSet = results[i];
|
||||
for (const test of resultSet.tests) {
|
||||
const path = this.getPathForTest(test, resultSet).join(sep);
|
||||
const path = getPathForTestInResult(test, resultSet).join(sep);
|
||||
if (isFailedState(test.ownComputedState)) {
|
||||
paths.add(path);
|
||||
} else {
|
||||
|
@ -906,7 +891,7 @@ abstract class RunOrDebugLastRun extends RunOrDebugExtsById {
|
|||
|
||||
for (const test of lastResult.tests) {
|
||||
if (test.direct) {
|
||||
yield this.getPathForTest(test, lastResult);
|
||||
yield getPathForTestInResult(test, lastResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { DefaultKeyboardNavigationDelegate, IListAccessibilityProvider } from 'v
|
|||
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { ITreeContextMenuEvent, ITreeEvent, ITreeFilter, ITreeNode, ITreeRenderer, ITreeSorter, TreeFilterResult, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { disposableTimeout, RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Color, RGBA } from 'vs/base/common/color';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
|
@ -53,10 +53,12 @@ import { testingHiddenIcon, testingStatesToIcons } from 'vs/workbench/contrib/te
|
|||
import { ITestExplorerFilterState, TestExplorerFilterState, TestingExplorerFilter } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter';
|
||||
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
|
||||
import { ITestingProgressUiService } from 'vs/workbench/contrib/testing/browser/testingProgressUiService';
|
||||
import { getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration';
|
||||
import { TestExplorerStateFilter, TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { TestIdPath, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { cmpPriority, isFailedState, isStateWithResult } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { getPathForTestInResult, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { IWorkspaceTestCollectionService, TestSubscriptionListener } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
|
||||
|
@ -223,6 +225,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
public projection = this._register(new MutableDisposable<ITestTreeProjection>());
|
||||
|
||||
private readonly emptyTestsWidget: EmptyTestsWidget;
|
||||
private readonly revealTimeout = new MutableDisposable();
|
||||
private readonly _viewMode = TestingContextKeys.viewMode.bindTo(this.contextKeyService);
|
||||
private readonly _viewSorting = TestingContextKeys.viewSorting.bindTo(this.contextKeyService);
|
||||
|
||||
|
@ -272,6 +275,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
listContainer: HTMLElement,
|
||||
onDidChangeVisibility: Event<boolean>,
|
||||
private listener: TestSubscriptionListener | undefined,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@ITestService private readonly testService: ITestService,
|
||||
|
@ -379,6 +383,29 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
}
|
||||
}));
|
||||
|
||||
let followRunningTests = getTestingConfiguration(configurationService, TestingConfigKeys.FollowRunningTest);
|
||||
this._register(configurationService.onDidChangeConfiguration(() => {
|
||||
followRunningTests = getTestingConfiguration(configurationService, TestingConfigKeys.FollowRunningTest);
|
||||
}));
|
||||
|
||||
this._register(testResults.onTestChanged(evt => {
|
||||
if (!followRunningTests) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.reason !== TestResultItemChangeReason.OwnStateChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
// follow running tests, or tests whose state changed. Tests that
|
||||
// complete very fast may not enter the running state at all.
|
||||
if (evt.item.ownComputedState !== TestResultState.Running && !(evt.previous === TestResultState.Queued && isStateWithResult(evt.item.ownComputedState))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.revealByIdPath(getPathForTestInResult(evt.item, evt.result), false, false);
|
||||
}));
|
||||
|
||||
this._register(testResults.onResultsChanged(() => {
|
||||
this.tree.resort(null);
|
||||
}));
|
||||
|
@ -403,7 +430,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
* Tries to reveal by extension ID. Queues the request if the extension
|
||||
* ID is not currently available.
|
||||
*/
|
||||
private revealByIdPath(idPath: TestIdPath | undefined) {
|
||||
private revealByIdPath(idPath: TestIdPath | undefined, expand = true, focus = true) {
|
||||
if (!idPath) {
|
||||
this.hasPendingReveal = false;
|
||||
return;
|
||||
|
@ -423,19 +450,21 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
continue;
|
||||
}
|
||||
|
||||
// If this 'if' is true, we're at the clostest-visible parent to the node
|
||||
// If this 'if' is true, we're at the closest-visible parent to the node
|
||||
// we want to expand. Expand that, and then start the loop again because
|
||||
// we might already have children for it.
|
||||
if (i < idPath.length - 1) {
|
||||
this.tree.expand(element);
|
||||
expandToLevel = i + 1; // avoid an infinite loop if the test does not exist
|
||||
i = idPath.length - 1; // restart the loop since new children may now be visible
|
||||
continue;
|
||||
if (expand) {
|
||||
this.tree.expand(element);
|
||||
expandToLevel = i + 1; // avoid an infinite loop if the test does not exist
|
||||
i = idPath.length - 1; // restart the loop since new children may now be visible
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we've arrived!
|
||||
|
||||
// If the node or any of its children are exlcuded, flip on the 'show
|
||||
// If the node or any of its children are excluded, flip on the 'show
|
||||
// excluded tests' checkbox automatically.
|
||||
for (let n: TestItemTreeElement | TestTreeWorkspaceFolder = element; n instanceof TestItemTreeElement; n = n.parent) {
|
||||
if (n.test && this.testService.excludeTests.value.has(n.test.item.extId)) {
|
||||
|
@ -446,9 +475,11 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
|
||||
this.filterState.reveal.value = undefined;
|
||||
this.hasPendingReveal = false;
|
||||
this.tree.domFocus();
|
||||
if (focus) {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.revealTimeout.value = disposableTimeout(() => {
|
||||
// Don't scroll to the item if it's already visible
|
||||
if (this.tree.getRelativeTop(element) === null) {
|
||||
this.tree.reveal(element, 0.5);
|
||||
|
|
|
@ -12,6 +12,7 @@ export const enum TestingConfigKeys {
|
|||
AutoRunMode = 'testing.autoRun.mode',
|
||||
AutoOpenPeekView = 'testing.automaticallyOpenPeekView',
|
||||
AutoOpenPeekViewDuringAutoRun = 'testing.automaticallyOpenPeekViewDuringAutoRun',
|
||||
FollowRunningTest = 'testing.followRunningTest',
|
||||
}
|
||||
|
||||
export const enum AutoOpenPeekViewWhen {
|
||||
|
@ -65,6 +66,11 @@ export const testingConfiguation: IConfigurationNode = {
|
|||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
[TestingConfigKeys.FollowRunningTest]: {
|
||||
description: localize('testing.followRunningTest', 'Controls whether the running test should be followed in the test explorer view'),
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -73,6 +79,7 @@ export interface ITestingConfiguration {
|
|||
[TestingConfigKeys.AutoRunDelay]: number;
|
||||
[TestingConfigKeys.AutoOpenPeekView]: AutoOpenPeekViewWhen;
|
||||
[TestingConfigKeys.AutoOpenPeekViewDuringAutoRun]: boolean;
|
||||
[TestingConfigKeys.FollowRunningTest]: boolean;
|
||||
}
|
||||
|
||||
export const getTestingConfiguration = <K extends TestingConfigKeys>(config: IConfigurationService, key: K) => config.getValue<ITestingConfiguration[K]>(key);
|
||||
|
|
|
@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { TestResultState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IComputedStateAccessor, refreshComputedState } from 'vs/workbench/contrib/testing/common/getComputedState';
|
||||
import { ExtensionRunTestsRequest, ISerializedTestResults, ITestItem, ITestMessage, ITestRunTask, ITestTaskState, RunTestsRequest, TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ExtensionRunTestsRequest, ISerializedTestResults, ITestItem, ITestMessage, ITestRunTask, ITestTaskState, RunTestsRequest, TestIdPath, TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { maxPriority, statesInOrder } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
|
||||
export interface ITestResult {
|
||||
|
@ -63,6 +63,21 @@ export interface ITestResult {
|
|||
toJSON(): ISerializedTestResults | undefined;
|
||||
}
|
||||
|
||||
export const getPathForTestInResult = (test: TestResultItem, results: ITestResult): TestIdPath => {
|
||||
const path = [test];
|
||||
while (true) {
|
||||
const parentId = path[0].parent;
|
||||
const parent = parentId && results.getStateById(parentId);
|
||||
if (!parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
path.unshift(parent);
|
||||
}
|
||||
|
||||
return path.map(t => t.item.extId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Count of the number of tests in each run state.
|
||||
*/
|
||||
|
|
|
@ -23,6 +23,7 @@ export const statePriority: { [K in TestResultState]: number } = {
|
|||
};
|
||||
|
||||
export const isFailedState = (s: TestResultState) => s === TestResultState.Errored || s === TestResultState.Failed;
|
||||
export const isStateWithResult = (s: TestResultState) => s === TestResultState.Errored || s === TestResultState.Failed || s === TestResultState.Passed;
|
||||
|
||||
export const stateNodes = Object.entries(statePriority).reduce(
|
||||
(acc, [stateStr, priority]) => {
|
||||
|
|
Loading…
Reference in a new issue