mirror of
https://github.com/Microsoft/vscode
synced 2024-10-05 19:02:54 +00:00
parent
3b8141a1c4
commit
f91663e8ed
|
@ -30,6 +30,7 @@ import { testingConfiguation } from 'vs/workbench/contrib/testing/common/configu
|
|||
import { Testing } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { identifyTest, ITestIdWithSrc, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ITestProfileService, TestProfileService } from 'vs/workbench/contrib/testing/common/testConfigurationService';
|
||||
import { TestId, TestPosition } from 'vs/workbench/contrib/testing/common/testId';
|
||||
import { ITestingAutoRun, TestingAutoRun } from 'vs/workbench/contrib/testing/common/testingAutoRun';
|
||||
import { TestingContentProvider } from 'vs/workbench/contrib/testing/common/testingContentProvider';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
|
@ -142,9 +143,23 @@ CommandsRegistry.registerCommand({
|
|||
id: 'vscode.peekTestError',
|
||||
handler: async (accessor: ServicesAccessor, extId: string) => {
|
||||
const lookup = accessor.get(ITestResultService).getStateById(extId);
|
||||
if (lookup) {
|
||||
accessor.get(ITestingPeekOpener).tryPeekFirstError(lookup[0], lookup[1]);
|
||||
if (!lookup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [result, ownState] = lookup;
|
||||
const opener = accessor.get(ITestingPeekOpener);
|
||||
if (opener.tryPeekFirstError(result, ownState)) { // fast path
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const test of result.tests) {
|
||||
if (TestId.compare(ownState.item.extId, test.item.extId) === TestPosition.IsChild && opener.tryPeekFirstError(result, test)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ import { DefaultGutterClickAction, getTestingConfiguration, TestingConfigKeys }
|
|||
import { labelForTestInState } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { identifyTest, IncrementalTestCollectionItem, InternalTestItem, IRichLocation, ITestMessage, ITestRunProfile, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testConfigurationService';
|
||||
import { 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 { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { IMainThreadTestCollection, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService';
|
||||
|
||||
function isOriginalInDiffEditor(codeEditorService: ICodeEditorService, codeEditor: ICodeEditor): boolean {
|
||||
const diffEditors = codeEditorService.listDiffEditors();
|
||||
|
@ -402,7 +402,7 @@ abstract class RunTestDecoration extends Disposable {
|
|||
/**
|
||||
* Gets context menu actions relevant for a singel test.
|
||||
*/
|
||||
protected getTestContextMenuActions(collection: IMainThreadTestCollection, test: InternalTestItem) {
|
||||
protected getTestContextMenuActions(test: InternalTestItem, resultItem?: TestResultItem) {
|
||||
const testActions: IAction[] = [];
|
||||
const capabilities = this.testProfileService.controllerCapabilities(test.controllerId);
|
||||
if (capabilities & TestRunProfileBitset.Run) {
|
||||
|
@ -437,6 +437,11 @@ abstract class RunTestDecoration extends Disposable {
|
|||
}));
|
||||
}
|
||||
|
||||
if (resultItem && isFailedState(resultItem.computedState)) {
|
||||
testActions.push(new Action('testing.gutter.peekFailure', localize('peek failure', 'Peek Error'), undefined, undefined,
|
||||
() => this.commandService.executeCommand('vscode.peekTestError', test.item.extId)));
|
||||
}
|
||||
|
||||
testActions.push(new Action('testing.gutter.reveal', localize('reveal test', 'Reveal in Test Explorer'), undefined, undefined,
|
||||
() => this.commandService.executeCommand('vscode.revealTestInExplorer', test.item.extId)));
|
||||
|
||||
|
@ -476,8 +481,8 @@ class MultiRunTestDecoration extends RunTestDecoration implements ITestDecoratio
|
|||
allActions.push(new Action('testing.gutter.debugAll', localize('debug all test', 'Debug All Tests'), undefined, undefined, () => this.defaultDebug()));
|
||||
}
|
||||
|
||||
const testSubmenus = this.tests.map(({ test }) =>
|
||||
new SubmenuAction(test.item.extId, test.item.label, this.getTestContextMenuActions(this.testService.collection, test)));
|
||||
const testSubmenus = this.tests.map(({ test, resultItem }) =>
|
||||
new SubmenuAction(test.item.extId, test.item.label, this.getTestContextMenuActions(test, resultItem)));
|
||||
|
||||
return Separator.join(allActions, testSubmenus);
|
||||
}
|
||||
|
@ -519,7 +524,7 @@ class RunSingleTestDecoration extends RunTestDecoration implements ITestDecorati
|
|||
}
|
||||
|
||||
protected override getContextMenuActions(e: IEditorMouseEvent) {
|
||||
return this.getTestContextMenuActions(this.testService.collection, this.test);
|
||||
return this.getTestContextMenuActions(this.test, this.resultItem);
|
||||
}
|
||||
|
||||
protected override defaultRun() {
|
||||
|
|
|
@ -470,7 +470,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
}
|
||||
}));
|
||||
|
||||
this._register(this.tree.onDidChangeSelection(async evt => {
|
||||
this._register(this.tree.onDidChangeSelection(evt => {
|
||||
if (evt.browserEvent instanceof MouseEvent && (evt.browserEvent.altKey || evt.browserEvent.shiftKey)) {
|
||||
return; // don't focus when alt-clicking to multi select
|
||||
}
|
||||
|
@ -478,7 +478,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
const selected = evt.elements[0];
|
||||
if (selected && evt.browserEvent && selected instanceof TestItemTreeElement
|
||||
&& selected.children.size === 0 && selected.test.expand === TestItemExpandState.NotExpandable) {
|
||||
await this.tryPeekError(selected);
|
||||
this.tryPeekError(selected);
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -629,7 +629,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
/**
|
||||
* Tries to peek the first test error, if the item is in a failed state.
|
||||
*/
|
||||
private async tryPeekError(item: TestItemTreeElement) {
|
||||
private tryPeekError(item: TestItemTreeElement) {
|
||||
const lookup = item.test && this.testResults.getStateById(item.test.item.extId);
|
||||
return lookup && lookup[1].tasks.some(s => isFailedState(s.state))
|
||||
? this.peekOpener.tryPeekFirstError(lookup[0], lookup[1], { preserveFocus: true })
|
||||
|
|
|
@ -154,14 +154,14 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener
|
|||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public async tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial<ITextEditorOptions>) {
|
||||
public tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial<ITextEditorOptions>) {
|
||||
const candidate = this.getFailedCandidateMessage(test);
|
||||
if (!candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const message = candidate.message;
|
||||
return this.showPeekFromUri({
|
||||
this.showPeekFromUri({
|
||||
type: TestUriType.ResultMessage,
|
||||
documentUri: message.location!.uri,
|
||||
taskIndex: candidate.taskId,
|
||||
|
@ -169,6 +169,7 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener
|
|||
resultId: result.id,
|
||||
testExtId: test.item.extId,
|
||||
}, { selection: message.location!.range, ...options });
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
|
|
|
@ -69,6 +69,25 @@ export class TestId {
|
|||
return base.toString() + TestIdPathParts.Delimiter + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the position of the two ID strings.
|
||||
*/
|
||||
public static compare(a: string, b: string) {
|
||||
if (a === b) {
|
||||
return TestPosition.IsSame;
|
||||
}
|
||||
|
||||
if (b.startsWith(a + TestIdPathParts.Delimiter)) {
|
||||
return TestPosition.IsChild;
|
||||
}
|
||||
|
||||
if (a.startsWith(b + TestIdPathParts.Delimiter)) {
|
||||
return TestPosition.IsParent;
|
||||
}
|
||||
|
||||
return TestPosition.Disconnected;
|
||||
}
|
||||
|
||||
constructor(
|
||||
public readonly path: readonly string[],
|
||||
private readonly viewEnd = path.length,
|
||||
|
@ -124,7 +143,11 @@ export class TestId {
|
|||
/**
|
||||
* Compares the other test ID with this one.
|
||||
*/
|
||||
public compare(other: TestId) {
|
||||
public compare(other: TestId | string) {
|
||||
if (typeof other === 'string') {
|
||||
return TestId.compare(this.toString(), other);
|
||||
}
|
||||
|
||||
for (let i = 0; i < other.viewEnd && i < this.viewEnd; i++) {
|
||||
if (other.path[i] !== this.path[i]) {
|
||||
return TestPosition.Disconnected;
|
||||
|
|
|
@ -15,7 +15,7 @@ export interface ITestingPeekOpener {
|
|||
* Tries to peek the first test error, if the item is in a failed state.
|
||||
* @returns a boolean indicating whether a peek was opened
|
||||
*/
|
||||
tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial<ITextEditorOptions>): Promise<boolean>;
|
||||
tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial<ITextEditorOptions>): boolean;
|
||||
|
||||
/**
|
||||
* Opens the peek. Shows any available message.
|
||||
|
|
Loading…
Reference in a new issue