testing: additional actions and better theming for peek

Fixes #124642
This commit is contained in:
Connor Peet
2021-05-26 13:00:37 -07:00
parent a5bbc53e88
commit 244b48af73
6 changed files with 190 additions and 85 deletions
@@ -26,7 +26,7 @@
.test-explorer .monaco-list-row .codicon-testing-hidden {
display: none;
flex-shrink: 0;
margin-right: 1px;
margin-right: 0.8em;
}
.test-explorer .monaco-list-row:hover .monaco-action-bar,
@@ -128,7 +128,8 @@
border-bottom-width: 2px;
}
.monaco-editor .zone-widget.test-output-peek .test-output-peek-message-container {
.monaco-editor .zone-widget.test-output-peek .test-output-peek-message-container,
.monaco-editor .zone-widget.test-output-peek .test-output-peek-tree {
height: 100%;
}
@@ -144,6 +145,7 @@
.monaco-editor .zone-widget.test-output-peek .preview-text p:last-child {
margin-bottom: 0;
}
/** -- filter */
.testing-filter-action-bar {
flex-shrink: 0;
@@ -8,7 +8,6 @@ import { Codicon } from 'vs/base/common/codicons';
import { Iterable } from 'vs/base/common/iterator';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { isDefined } from 'vs/base/common/types';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { localize } from 'vs/nls';
@@ -31,12 +30,12 @@ import { IActionableTestTreeElement, TestItemTreeElement } from 'vs/workbench/co
import * as icons from 'vs/workbench/contrib/testing/browser/icons';
import { ITestExplorerFilterState } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter';
import { TestingExplorerView, TestingExplorerViewModel } from 'vs/workbench/contrib/testing/browser/testingExplorerView';
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 } 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 { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates';
import { getPathForTestInResult, ITestResult } from 'vs/workbench/contrib/testing/common/testResult';
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
@@ -625,6 +624,7 @@ export class GoToTest extends Action2 {
const { range, uri, extId } = element.test.item;
accessor.get(ITestExplorerFilterState).reveal.value = [extId];
accessor.get(ITestingPeekOpener).closeAllPeeks();
let isFile = true;
try {
@@ -640,7 +640,7 @@ export class GoToTest extends Action2 {
return;
}
const pane = await editorService.openEditor({
await editorService.openEditor({
resource: uri,
options: {
selection: range
@@ -649,12 +649,6 @@ export class GoToTest extends Action2 {
preserveFocus: preserveFocus === true,
},
});
// if the user selected a failed test and now they didn't, hide the peek
const control = pane?.getControl();
if (isCodeEditor(control)) {
TestingOutputPeekController.get(control).removePeek();
}
}
/**
@@ -680,6 +674,7 @@ export class GoToTest extends Action2 {
const editorService = accessor.get(IEditorService);
accessor.get(ITestExplorerFilterState).reveal.value = [test.extId];
accessor.get(ITestingPeekOpener).closeAllPeeks();
let isFile = true;
try {
@@ -695,7 +690,7 @@ export class GoToTest extends Action2 {
return;
}
const pane = await editorService.openEditor({
await editorService.openEditor({
resource: test.uri,
options: {
selection: test.range
@@ -704,12 +699,6 @@ export class GoToTest extends Action2 {
preserveFocus,
},
});
// if the user selected a failed test and now they didn't, hide the peek
const control = pane?.getControl();
if (isCodeEditor(control)) {
TestingOutputPeekController.get(control).removePeek();
}
}
}
@@ -954,41 +943,47 @@ export class DebugCurrentFile extends RunOrDebugCurrentFile {
}
}
abstract class RunOrDebugExtsById extends Action2 {
export const runTestsByPath = async (
workspaceTests: IWorkspaceTestCollectionService,
progress: IProgressService,
paths: ReadonlyArray<TestIdPath>,
runTests: (tests: ReadonlyArray<InternalTestItem>) => Promise<ITestResult>,
): Promise<ITestResult | undefined> => {
const subscription = workspaceTests.subscribeToWorkspaceTests();
try {
const todo = Promise.all([...subscription.workspaceFolderCollections.values()].map(
c => Promise.all(paths.map(p => getTestByPath(c, p))),
));
const tests = flatten(await showDiscoveringWhile(progress, todo)).filter(isDefined);
return tests.length ? await runTests(tests) : undefined;
} finally {
subscription.dispose();
}
};
abstract class RunOrDebugExtsByPath extends Action2 {
/**
* @override
*/
public async run(accessor: ServicesAccessor) {
public async run(accessor: ServicesAccessor, ...args: unknown[]) {
const testService = accessor.get(ITestService);
const paths = [...this.getTestExtIdsToRun(accessor)];
if (paths.length === 0) {
return;
}
const workspaceTests = accessor.get(IWorkspaceTestCollectionService).subscribeToWorkspaceTests();
try {
const todo = Promise.all([...workspaceTests.workspaceFolderCollections.values()].map(
c => Promise.all(paths.map(p => getTestByPath(c, p))),
));
const tests = flatten(await showDiscoveringWhile(accessor.get(IProgressService), todo)).filter(isDefined);
if (tests.length) {
await this.runTest(testService, tests);
}
} finally {
workspaceTests.dispose();
}
await runTestsByPath(
accessor.get(IWorkspaceTestCollectionService),
accessor.get(IProgressService),
[...this.getTestExtIdsToRun(accessor, ...args)],
tests => this.runTest(testService, tests),
);
}
protected abstract getTestExtIdsToRun(accessor: ServicesAccessor): Iterable<TestIdPath>;
protected abstract getTestExtIdsToRun(accessor: ServicesAccessor, ...args: unknown[]): Iterable<TestIdPath>;
protected abstract filter(node: InternalTestItem): boolean;
protected abstract runTest(service: ITestService, node: InternalTestItem[]): Promise<ITestResult>;
protected abstract runTest(service: ITestService, node: readonly InternalTestItem[]): Promise<ITestResult>;
}
abstract class RunOrDebugFailedTests extends RunOrDebugExtsById {
abstract class RunOrDebugFailedTests extends RunOrDebugExtsByPath {
/**
* @inheritdoc
*/
@@ -1012,12 +1007,13 @@ abstract class RunOrDebugFailedTests extends RunOrDebugExtsById {
}
}
abstract class RunOrDebugLastRun extends RunOrDebugExtsById {
abstract class RunOrDebugLastRun extends RunOrDebugExtsByPath {
/**
* @inheritdoc
*/
protected *getTestExtIdsToRun(accessor: ServicesAccessor): Iterable<TestIdPath> {
const lastResult = accessor.get(ITestResultService).results[0];
protected *getTestExtIdsToRun(accessor: ServicesAccessor, runId?: string): Iterable<TestIdPath> {
const resultService = accessor.get(ITestResultService);
const lastResult = runId ? resultService.results.find(r => r.id === runId) : resultService.results[0];
if (!lastResult) {
return;
}
@@ -12,6 +12,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { Extensions as ViewContainerExtensions, IViewContainersRegistry, IViewsRegistry, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
@@ -19,23 +20,24 @@ import { testingViewIcon } from 'vs/workbench/contrib/testing/browser/icons';
import { TestingDecorations } from 'vs/workbench/contrib/testing/browser/testingDecorations';
import { ITestExplorerFilterState, TestExplorerFilterState } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter';
import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView';
import { CloseTestPeek, ITestingPeekOpener, TestingOutputPeekController, TestingPeekOpener } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
import { CloseTestPeek, TestingOutputPeekController, TestingPeekOpener } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
import { ITestingOutputTerminalService, TestingOutputTerminalService } from 'vs/workbench/contrib/testing/browser/testingOutputTerminalService';
import { ITestingProgressUiService, TestingProgressUiService } from 'vs/workbench/contrib/testing/browser/testingProgressUiService';
import { TestingViewPaneContainer } from 'vs/workbench/contrib/testing/browser/testingViewPaneContainer';
import { testingConfiguation } from 'vs/workbench/contrib/testing/common/configuration';
import { Testing } from 'vs/workbench/contrib/testing/common/constants';
import { TestIdPath, TestIdWithSrc } from 'vs/workbench/contrib/testing/common/testCollection';
import { TestIdPath, TestIdWithMaybeSrc, TestIdWithSrc } from 'vs/workbench/contrib/testing/common/testCollection';
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';
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
import { ITestResultService, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
import { ITestResultStorage, TestResultStorage } from 'vs/workbench/contrib/testing/common/testResultStorage';
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { TestService } from 'vs/workbench/contrib/testing/common/testServiceImpl';
import { IWorkspaceTestCollectionService, WorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { allTestActions } from './testExplorerActions';
import { allTestActions, runTestsByPath } from './testExplorerActions';
registerSingleton(ITestService, TestService);
registerSingleton(ITestResultStorage, TestResultStorage);
@@ -108,9 +110,9 @@ registerEditorContribution(Testing.DecorationsContributionId, TestingDecorations
CommandsRegistry.registerCommand({
id: 'vscode.runTests',
handler: async (accessor: ServicesAccessor, tests: TestIdWithSrc[]) => {
handler: async (accessor: ServicesAccessor, tests: TestIdWithMaybeSrc[]) => {
const testService = accessor.get(ITestService);
testService.runTests({ debug: false, tests: tests.filter(t => t.src && t.testId) });
testService.runTests({ debug: false, tests: tests.filter(t => !!t.testId) });
}
});
@@ -140,4 +142,20 @@ CommandsRegistry.registerCommand({
}
});
CommandsRegistry.registerCommand({
id: 'vscode.runTestsByPath',
handler: async (accessor: ServicesAccessor, debug: boolean, ...pathToTests: TestIdPath[]) => {
const testService = accessor.get(ITestService);
await runTestsByPath(
accessor.get(IWorkspaceTestCollectionService),
accessor.get(IProgressService),
pathToTests,
tests => testService.runTests({
debug: false,
tests: tests.map(t => ({ testId: t.item.extId, src: t.src })),
}),
);
}
});
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration(testingConfiguation);
@@ -51,7 +51,7 @@ import { HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/brows
import { IActionableTestTreeElement, isActionableTestTreeElement, ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage, TestTreeWorkspaceFolder } from 'vs/workbench/contrib/testing/browser/explorerProjections/index';
import { testingHiddenIcon, testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons';
import { ITestExplorerFilterState, TestExplorerFilterState, TestingExplorerFilter } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter';
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
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';
@@ -34,7 +34,7 @@ import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/br
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
import { getOuterEditor, IPeekViewService, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from 'vs/editor/contrib/peekView/peekView';
import { getOuterEditor, IPeekViewService, peekViewResultsBackground, peekViewResultsMatchForeground, peekViewResultsSelectionBackground, peekViewResultsSelectionForeground, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from 'vs/editor/contrib/peekView/peekView';
import { localize } from 'vs/nls';
import { createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
@@ -43,21 +43,23 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { createDecorator, IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService';
import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { TestResultState } from 'vs/workbench/api/common/extHostTypes';
import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels';
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display';
import { testingStatesToIcons, testMessageSeverityToIcons } from 'vs/workbench/contrib/testing/browser/icons';
import * as icons from 'vs/workbench/contrib/testing/browser/icons';
import { ITestingOutputTerminalService } from 'vs/workbench/contrib/testing/browser/testingOutputTerminalService';
import { testingPeekBorder } from 'vs/workbench/contrib/testing/browser/theme';
import { AutoOpenPeekViewWhen, getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration';
import { Testing } from 'vs/workbench/contrib/testing/common/constants';
import { IRichLocation, ITestItem, ITestMessage, ITestRunTask, TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates';
import { buildTestUri, parseTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri';
import { getPathForTestInResult, ITestResult, maxCountPriority, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
@@ -84,18 +86,6 @@ class TestDto {
}
}
export interface ITestingPeekOpener {
_serviceBrand: undefined;
/**
* 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>;
}
export const ITestingPeekOpener = createDecorator<ITestingPeekOpener>('testingPeekOpener');
export class TestingPeekOpener extends Disposable implements ITestingPeekOpener {
declare _serviceBrand: undefined;
@@ -109,10 +99,7 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener
this._register(testResults.onTestChanged(this.openPeekOnFailure, this));
}
/**
* Tries to peek the first test error, if the item is in a failed state.
* @returns a boolean if a peek was opened
*/
/** @inheritdoc */
public async tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial<ITextEditorOptions>) {
const candidate = this.getCandidateMessage(test);
if (!candidate) {
@@ -141,6 +128,13 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener
return true;
}
/** @inheritdoc */
public closeAllPeeks() {
for (const editor of this.codeEditorService.listCodeEditors()) {
TestingOutputPeekController.get(editor).removePeek();
}
}
/**
* Opens the peek view on a test failure, based on user preferences.
*/
@@ -766,7 +760,7 @@ export class TestResultElement implements ITreeElement {
public readonly label = this.value.name;
public get icon() {
return testingStatesToIcons.get(
return icons.testingStatesToIcons.get(
this.value.completedAt === undefined
? TestResultState.Running
: maxCountPriority(this.value.counts)
@@ -778,22 +772,22 @@ export class TestResultElement implements ITreeElement {
export class TestCaseElement implements ITreeElement {
public readonly type = 'test';
public readonly context = this.value.item.extId;
public readonly id = `${this.results.id}/${this.value.item.extId}`;
public readonly label = this.value.item.label;
public readonly context = this.test.item.extId;
public readonly id = `${this.results.id}/${this.test.item.extId}`;
public readonly label = this.test.item.label;
public readonly description?: string;
public get icon() {
return testingStatesToIcons.get(this.value.computedState);
return icons.testingStatesToIcons.get(this.test.computedState);
}
public get path() {
return getPathForTestInResult(this.value, this.results);
return getPathForTestInResult(this.test, this.results);
}
constructor(
private readonly results: ITestResult,
public readonly value: TestResultItem,
public readonly test: TestResultItem,
) {
for (const parent of this.parents()) {
this.description = this.description
@@ -804,7 +798,7 @@ export class TestCaseElement implements ITreeElement {
private *parents() {
for (
let parent = this.value.parent && this.results.getStateById(this.value.parent);
let parent = this.test.parent && this.results.getStateById(this.test.parent);
parent;
parent = parent.parent && this.results.getStateById(parent.parent)
) {
@@ -825,7 +819,7 @@ class TestTaskElement implements ITreeElement {
return getPathForTestInResult(this.test, this.results);
}
constructor(private readonly results: ITestResult, private readonly test: TestResultItem, index: number) {
constructor(private readonly results: ITestResult, public readonly test: TestResultItem, index: number) {
this.id = `${results.id}/${test.item.extId}/${index}`;
this.task = results.tasks[index];
this.context = String(index);
@@ -861,7 +855,7 @@ class TestMessageElement implements ITreeElement {
this.id = this.uri.toString();
this.label = firstLine(renderStringAsPlaintext(message));
this.icon = testMessageSeverityToIcons.get(severity);
this.icon = icons.testMessageSeverityToIcons.get(severity);
}
}
@@ -1155,9 +1149,9 @@ class TreeActionsProvider {
) { }
public provideActionBar(element: ITreeElement) {
const test = element instanceof TestCaseElement ? element.value : undefined;
const test = element instanceof TestCaseElement ? element.test : undefined;
const contextOverlay = this.contextKeyService.createOverlay([
['view', Testing.OutputPeekContributionId],
['peek', Testing.OutputPeekContributionId],
[TestingContextKeys.peekItemType.key, element.type],
[TestingContextKeys.testItemExtId.key, test?.item.extId],
[TestingContextKeys.testItemHasUri.key, !!test?.item.uri],
@@ -1172,23 +1166,62 @@ class TreeActionsProvider {
if (element instanceof TestResultElement) {
primary.push(new Action(
'testing.showResultOutput',
'testing.outputPeek.showResultOutput',
localize('testing.showResultOutput', "Show Result Output"),
Codicon.terminal.classNames,
undefined,
() => this.testTerminalService.open(element.value)
));
primary.push(new Action(
'testing.outputPeek.reRunLastRun',
localize('testing.reRunLastRun', "Rerun Test Run"),
ThemeIcon.asClassName(icons.testingRunIcon),
undefined,
() => this.commandService.executeCommand('testing.reRunLastRun', element.value.id),
));
if (Iterable.some(element.value.tests, t => t.item.debuggable)) {
primary.push(new Action(
'testing.outputPeek.debugLastRun',
localize('testing.debugLastRun', "Debug Test Run"),
ThemeIcon.asClassName(icons.testingDebugIcon),
undefined,
() => this.commandService.executeCommand('testing.debugLastRun', element.value.id),
));
}
}
if (element instanceof TestCaseElement || element instanceof TestTaskElement) {
primary.push(new Action(
'testing.revealInExplorer',
'testing.outputPeek.revealInExplorer',
localize('testing.revealInExplorer', "Reveal in Test Explorer"),
Codicon.listTree.classNames,
undefined,
() => this.commandService.executeCommand('vscode.revealTestInExplorer', element.path),
));
if (element.test.item.runnable) {
primary.push(new Action(
'testing.outputPeek.runTest',
localize('run test', 'Run Test'),
ThemeIcon.asClassName(icons.testingRunIcon),
undefined,
() => this.commandService.executeCommand('vscode.runTestsByPath', false, element.path),
));
}
if (element.test.item.debuggable) {
primary.push(new Action(
'testing.outputPeek.debugTest',
localize('debug test', 'Debug Test'),
ThemeIcon.asClassName(icons.testingDebugIcon),
undefined,
() => this.commandService.executeCommand('vscode.runTestsByPath', true, element.path),
));
}
}
const result = { primary, secondary };
const actionsDisposable = createAndFillInActionBarActions(menu, {
shouldForwardArgs: true,
@@ -1200,3 +1233,32 @@ class TreeActionsProvider {
}
}
}
registerThemingParticipant((theme, collector) => {
const resultsBackground = theme.getColor(peekViewResultsBackground);
if (resultsBackground) {
collector.addRule(`.monaco-editor .test-output-peek .test-output-peek-tree { background-color: ${resultsBackground}; }`);
}
const resultsMatchForeground = theme.getColor(peekViewResultsMatchForeground);
if (resultsMatchForeground) {
collector.addRule(`.monaco-editor .test-output-peek .test-output-peek-tree { color: ${resultsMatchForeground}; }`);
}
const resultsSelectedBackground = theme.getColor(peekViewResultsSelectionBackground);
if (resultsSelectedBackground) {
collector.addRule(`.monaco-editor .test-output-peek .test-output-peek-tree .monaco-list:focus .monaco-list-rows > .monaco-list-row.selected:not(.highlighted) { background-color: ${resultsSelectedBackground}; }`);
}
const resultsSelectedForeground = theme.getColor(peekViewResultsSelectionForeground);
if (resultsSelectedForeground) {
collector.addRule(`.monaco-editor .test-output-peek .test-output-peek-tree .monaco-list:focus .monaco-list-rows > .monaco-list-row.selected:not(.highlighted) { color: ${resultsSelectedForeground} !important; }`);
}
const textLinkForegroundColor = theme.getColor(textLinkForeground);
if (textLinkForegroundColor) {
collector.addRule(`.monaco-editor .test-output-peek .test-output-peek-message-container a { color: ${textLinkForegroundColor}; }`);
}
const textLinkActiveForegroundColor = theme.getColor(textLinkActiveForeground);
if (textLinkActiveForegroundColor) {
collector.addRule(`.monaco-editor .test-output-peek .test-output-peek-message-container a :hover { color: ${textLinkActiveForegroundColor}; }`);
}
});
@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult';
export interface ITestingPeekOpener {
_serviceBrand: undefined;
/**
* 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>;
/**
* Closes peeks for all visible editors.
*/
closeAllPeeks(): void;
}
export const ITestingPeekOpener = createDecorator<ITestingPeekOpener>('testingPeekOpener');