Skip to content

Commit d975a01

Browse files
committed
[vscode] Support TestMessage#contextValue
Also adds the menu mapping for testing/message/context vscode menu extension. contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger <[email protected]>
1 parent ad2e106 commit d975a01

File tree

12 files changed

+172
-5
lines changed

12 files changed

+172
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
- [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/)
66

7+
## v1.45.0 - 12/21/2023
8+
9+
- [plugin] Support TestMessage.contextValue from vscode API [#13176](https://github.com/eclipse-theia/theia/pull/13176) - contributed on behalf of STMicroelectronics
10+
711
## v1.44.0 - 11/30/2023
812

913
- [application-manager] added option to copy `trash` dependency to the bundle [#13112](https://github.com/eclipse-theia/theia/pull/13112)

packages/plugin-ext/src/common/test-types.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export interface TestMessageDTO {
8484
readonly actual?: string;
8585
readonly location?: Location;
8686
readonly message: string | MarkdownString;
87+
readonly contextValue?: string;
8788
}
8889

8990
export interface TestItemDTO {
@@ -131,3 +132,23 @@ export namespace TestItemReference {
131132
}
132133
}
133134

135+
export interface TestMessageArg {
136+
typeTag: '$type_test_message_arg',
137+
testItemReference: TestItemReference | undefined,
138+
testMessage: TestMessageDTO
139+
}
140+
141+
export namespace TestMessageArg {
142+
export function is(arg: unknown): arg is TestMessageArg {
143+
return isObject<TestMessageArg>(arg)
144+
&& arg.typeTag === '$type_test_message_arg';
145+
}
146+
147+
export function create(testItemReference: TestItemReference | undefined, testMessageDTO: TestMessageDTO): TestMessageArg {
148+
return {
149+
typeTag: '$type_test_message_arg',
150+
testItemReference: testItemReference,
151+
testMessage: testMessageDTO
152+
};
153+
}
154+
}

packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ import { ScmRepository } from '@theia/scm/lib/browser/scm-repository';
2323
import { ScmService } from '@theia/scm/lib/browser/scm-service';
2424
import { TimelineItem } from '@theia/timeline/lib/common/timeline-model';
2525
import { ScmCommandArg, TimelineCommandArg, TreeViewItemReference } from '../../../common';
26+
import { TestItemReference, TestMessageArg } from '../../../common/test-types';
2627
import { PluginScmProvider, PluginScmResource, PluginScmResourceGroup } from '../scm-main';
2728
import { TreeViewWidget } from '../view/tree-view-widget';
2829
import { CodeEditorWidgetUtil, codeToTheiaMappings, ContributionPoint } from './vscode-theia-menu-mappings';
2930
import { TAB_BAR_TOOLBAR_CONTEXT_MENU } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
31+
import { TestItem, TestMessage } from '@theia/test/lib/browser/test-service';
32+
import { fromLocation } from '../hierarchy/hierarchy-types-converters';
3033

3134
export type ArgumentAdapter = (...args: unknown[]) => unknown[];
3235

@@ -79,6 +82,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter {
7982
@postConstruct()
8083
protected init(): void {
8184
const toCommentArgs: ArgumentAdapter = (...args) => this.toCommentArgs(...args);
85+
const toTestMessageArgs: ArgumentAdapter = (...args) => this.toTestMessageArgs(...args);
8286
const firstArgOnly: ArgumentAdapter = (...args) => [args[0]];
8387
const noArgs: ArgumentAdapter = () => [];
8488
const toScmArgs: ArgumentAdapter = (...args) => this.toScmArgs(...args);
@@ -100,6 +104,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter {
100104
['scm/resourceGroup/context', toScmArgs],
101105
['scm/resourceState/context', toScmArgs],
102106
['scm/title', () => [this.toScmArg(this.scmService.selectedRepository)]],
107+
['testing/message/context', toTestMessageArgs],
103108
['timeline/item/context', (...args) => this.toTimelineArgs(...args)],
104109
['view/item/context', (...args) => this.toTreeArgs(...args)],
105110
['view/title', noArgs],
@@ -230,6 +235,30 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter {
230235
return timelineArgs;
231236
}
232237

238+
protected toTestMessageArgs(...args: any[]): any[] {
239+
let testItem: TestItem | undefined;
240+
let testMessage: TestMessage | undefined;
241+
for (const arg of args) {
242+
if (TestItem.is(arg)) {
243+
testItem = arg;
244+
} else if (Array.isArray(arg)) {
245+
testMessage = arg[0];
246+
}
247+
}
248+
if (testMessage) {
249+
const testItemReference = (testItem && testItem.controller) ? TestItemReference.create(testItem.controller.id, testItem.path) : undefined;
250+
const testMessageDTO = {
251+
message: testMessage.message,
252+
actual: testMessage.actual,
253+
expected: testMessage.expected,
254+
contextValue: testMessage.contextValue,
255+
location: testMessage.location ? fromLocation(testMessage.location) : undefined
256+
};
257+
return [TestMessageArg.create(testItemReference, testMessageDTO)];
258+
}
259+
return args;
260+
}
261+
233262
protected toTimelineArg(arg: TimelineItem): TimelineCommandArg {
234263
return {
235264
timelineHandle: arg.handle,

packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { VIEW_ITEM_CONTEXT_MENU } from '../view/tree-view-widget';
3232
import { WEBVIEW_CONTEXT_MENU, WebviewWidget } from '../webview/webview';
3333
import { EDITOR_LINENUMBER_CONTEXT_MENU } from '@theia/editor/lib/browser/editor-linenumber-contribution';
3434
import { TEST_VIEW_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-view-contribution';
35+
import { TEST_RUNS_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-run-view-contribution';
3536

3637
export const PLUGIN_EDITOR_TITLE_MENU = ['plugin_editor/title'];
3738
export const PLUGIN_EDITOR_TITLE_RUN_MENU = ['plugin_editor/title/run'];
@@ -57,6 +58,7 @@ export const implementedVSCodeContributionPoints = [
5758
'scm/title',
5859
'timeline/item/context',
5960
'testing/item/context',
61+
'testing/message/context',
6062
'view/item/context',
6163
'view/title',
6264
'webview/context'
@@ -83,6 +85,7 @@ export const codeToTheiaMappings = new Map<ContributionPoint, MenuPath[]>([
8385
['scm/resourceState/context', [ScmTreeWidget.RESOURCE_CONTEXT_MENU]],
8486
['scm/title', [PLUGIN_SCM_TITLE_MENU]],
8587
['testing/item/context', [TEST_VIEW_CONTEXT_MENU]],
88+
['testing/message/context', [TEST_RUNS_CONTEXT_MENU]],
8689
['timeline/item/context', [TIMELINE_ITEM_CONTEXT_MENU]],
8790
['view/item/context', [VIEW_ITEM_CONTEXT_MENU]],
8891
['view/title', [PLUGIN_VIEW_TITLE_MENU]],

packages/plugin-ext/src/plugin/tests.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ import { TestItemImpl, TestItemCollection } from './test-item';
4040
import { AccumulatingTreeDeltaEmitter, TreeDelta } from '@theia/test/lib/common/tree-delta';
4141
import {
4242
TestItemDTO, TestOutputDTO, TestExecutionState, TestRunProfileDTO,
43-
TestRunProfileKind, TestRunRequestDTO, TestStateChangeDTO, TestItemReference
43+
TestRunProfileKind, TestRunRequestDTO, TestStateChangeDTO, TestItemReference, TestMessageArg, TestMessageDTO
4444
} from '../common/test-types';
4545
import { ChangeBatcher, observableProperty } from '@theia/test/lib/common/collections';
4646
import { TestRunRequest } from './types-impl';
47+
import { MarkdownString } from '../common/plugin-api-rpc-model';
4748

4849
type RefreshHandler = (token: theia.CancellationToken) => void | theia.Thenable<void>;
4950
type ResolveHandler = (item: theia.TestItem | undefined) => theia.Thenable<void> | void;
@@ -335,6 +336,8 @@ export class TestingExtImpl implements TestingExt {
335336
return this.toTestItem(arg);
336337
} else if (Array.isArray(arg)) {
337338
return arg.map(param => TestItemReference.is(param) ? this.toTestItem(param) : param);
339+
} else if (TestMessageArg.is(arg)) {
340+
return this.fromTestMessageArg(arg);
338341
} else {
339342
return arg;
340343
}
@@ -343,6 +346,27 @@ export class TestingExtImpl implements TestingExt {
343346

344347
}
345348

349+
fromTestMessageArg(arg: TestMessageArg): { test?: theia.TestItem, message: theia.TestMessage } {
350+
const testItem = arg.testItemReference ? this.toTestItem(arg.testItemReference) : undefined;
351+
const message = this.toTestMessage(arg.testMessage);
352+
return {
353+
test: testItem,
354+
message: message
355+
};
356+
}
357+
358+
toTestMessage(testMessage: TestMessageDTO): theia.TestMessage {
359+
const message = MarkdownString.is(testMessage.message) ? Convert.toMarkdown(testMessage.message) : testMessage.message;
360+
361+
return {
362+
message: message,
363+
actualOutput: testMessage.actual,
364+
expectedOutput: testMessage.expected,
365+
contextValue: testMessage.contextValue,
366+
location: testMessage.location ? Convert.toLocation(testMessage.location) : undefined
367+
};
368+
}
369+
346370
toTestItem(ref: TestItemReference): theia.TestItem {
347371
const result = this.withController(ref.controllerId).items.find(ref.testPath);
348372
if (!result) {

packages/plugin-ext/src/plugin/type-converters.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1765,7 +1765,8 @@ export namespace TestMessage {
17651765
location: fromLocation(message.location),
17661766
message: fromMarkdown(message.message)!,
17671767
expected: message.expectedOutput,
1768-
actual: message.actualOutput
1768+
actual: message.actualOutput,
1769+
contextValue: message.contextValue
17691770
}];
17701771
}
17711772
}

packages/plugin-ext/src/plugin/types-impl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3293,6 +3293,7 @@ export class TestMessage implements theia.TestMessage {
32933293
public expectedOutput?: string;
32943294
public actualOutput?: string;
32953295
public location?: theia.Location;
3296+
public contextValue?: string;
32963297

32973298
public static diff(message: string | theia.MarkdownString, expected: string, actual: string): theia.TestMessage {
32983299
const msg = new TestMessage(message);

packages/plugin/src/theia.d.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16448,6 +16448,37 @@ export module '@theia/plugin' {
1644816448
*/
1644916449
location?: Location;
1645016450

16451+
/**
16452+
* Context value of the test item. This can be used to contribute message-
16453+
* specific actions to the test peek view. The value set here can be found
16454+
* in the `testMessage` property of the following `menus` contribution points:
16455+
*
16456+
* - `testing/message/context` - context menu for the message in the results tree
16457+
* - `testing/message/content` - a prominent button overlaying editor content where
16458+
* the message is displayed.
16459+
*
16460+
* For example:
16461+
*
16462+
* ```json
16463+
* "contributes": {
16464+
* "menus": {
16465+
* "testing/message/content": [
16466+
* {
16467+
* "command": "extension.deleteCommentThread",
16468+
* "when": "testMessage == canApplyRichDiff"
16469+
* }
16470+
* ]
16471+
* }
16472+
* }
16473+
* ```
16474+
*
16475+
* The command will be called with an object containing:
16476+
* - `test`: the {@link TestItem} the message is associated with, *if* it
16477+
* is still present in the {@link TestController.items} collection.
16478+
* - `message`: the {@link TestMessage} instance.
16479+
*/
16480+
contextValue?: string;
16481+
1645116482
/**
1645216483
* Creates a new TestMessage that will present as a diff in the editor.
1645316484
* @param message Message to display to the user.

packages/test/src/browser/test-service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface TestMessage {
5858
readonly actual?: string;
5959
readonly location: Location;
6060
readonly message: string | MarkdownString;
61+
readonly contextValue?: string;
6162
}
6263

6364
export interface TestState {
@@ -136,6 +137,7 @@ export interface TestItem {
136137
readonly controller: TestController | undefined;
137138
readonly canResolveChildren: boolean;
138139
resolveChildren(): void;
140+
readonly path: string[];
139141
}
140142

141143
export namespace TestItem {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// *****************************************************************************
2+
// Copyright (C) 2023 STMicroelectronics and others.
3+
//
4+
// This program and the accompanying materials are made available under the
5+
// terms of the Eclipse Public License v. 2.0 which is available at
6+
// http://www.eclipse.org/legal/epl-2.0.
7+
//
8+
// This Source Code may also be made available under the following Secondary
9+
// Licenses when the conditions for such availability set forth in the Eclipse
10+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
// with the GNU Classpath Exception which is available at
12+
// https://www.gnu.org/software/classpath/license.html.
13+
//
14+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15+
// *****************************************************************************
16+
17+
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
18+
import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service';
19+
20+
@injectable()
21+
export class TestContextKeyService {
22+
23+
@inject(ContextKeyService)
24+
protected readonly contextKeyService: ContextKeyService;
25+
26+
protected _contextValue: ContextKey<string | undefined>;
27+
get contextValue(): ContextKey<string | undefined> {
28+
return this._contextValue;
29+
}
30+
31+
@postConstruct()
32+
protected init(): void {
33+
this._contextValue = this.contextKeyService.createKey<string | undefined>('testMessage', undefined);
34+
}
35+
36+
}

0 commit comments

Comments
 (0)