@theia/plugin-ext 1.42.0 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/common/arrays.d.ts +4 -0
- package/lib/common/arrays.d.ts.map +1 -1
- package/lib/common/arrays.js +15 -1
- package/lib/common/arrays.js.map +1 -1
- package/lib/common/commands.d.ts +4 -0
- package/lib/common/commands.d.ts.map +1 -0
- package/lib/common/commands.js +17 -0
- package/lib/common/commands.js.map +1 -0
- package/lib/common/plugin-api-rpc-model.d.ts +1 -0
- package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc-model.js.map +1 -1
- package/lib/common/plugin-api-rpc.d.ts +48 -6
- package/lib/common/plugin-api-rpc.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc.js +3 -2
- package/lib/common/plugin-api-rpc.js.map +1 -1
- package/lib/common/plugin-protocol.d.ts +27 -3
- package/lib/common/plugin-protocol.d.ts.map +1 -1
- package/lib/common/plugin-protocol.js +8 -1
- package/lib/common/plugin-protocol.js.map +1 -1
- package/lib/common/test-types.d.ts +83 -0
- package/lib/common/test-types.d.ts.map +1 -0
- package/lib/common/test-types.js +40 -0
- package/lib/common/test-types.js.map +1 -0
- package/lib/hosted/browser/hosted-plugin.d.ts +4 -1
- package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/browser/hosted-plugin.js +11 -0
- package/lib/hosted/browser/hosted-plugin.js.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.js +71 -33
- package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
- package/lib/hosted/node/plugin-reader.d.ts.map +1 -1
- package/lib/hosted/node/plugin-reader.js +4 -2
- package/lib/hosted/node/plugin-reader.js.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.d.ts +5 -4
- package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.js +79 -19
- package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
- package/lib/main/browser/command-registry-main.d.ts +3 -0
- package/lib/main/browser/command-registry-main.d.ts.map +1 -1
- package/lib/main/browser/command-registry-main.js +11 -1
- package/lib/main/browser/command-registry-main.js.map +1 -1
- package/lib/main/browser/languages-main.d.ts.map +1 -1
- package/lib/main/browser/languages-main.js +7 -5
- package/lib/main/browser/languages-main.js.map +1 -1
- package/lib/main/browser/main-context.d.ts.map +1 -1
- package/lib/main/browser/main-context.js +3 -0
- package/lib/main/browser/main-context.js.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +2 -2
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js +3 -0
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
- package/lib/main/browser/notebooks/notebook-documents-and-editors-main.js +2 -2
- package/lib/main/browser/notebooks/notebook-documents-and-editors-main.js.map +1 -1
- package/lib/main/browser/notebooks/notebook-documents-main.js +1 -1
- package/lib/main/browser/notebooks/notebook-documents-main.js.map +1 -1
- package/lib/main/browser/notebooks/notebook-dto.js +2 -2
- package/lib/main/browser/notebooks/notebook-dto.js.map +1 -1
- package/lib/main/browser/notebooks/notebook-kernels-main.d.ts.map +1 -1
- package/lib/main/browser/notebooks/notebook-kernels-main.js +4 -10
- package/lib/main/browser/notebooks/notebook-kernels-main.js.map +1 -1
- package/lib/main/browser/notebooks/notebook-renderers-main.js +1 -1
- package/lib/main/browser/notebooks/notebook-renderers-main.js.map +1 -1
- package/lib/main/browser/notebooks/notebooks-main.d.ts +2 -2
- package/lib/main/browser/notebooks/notebooks-main.d.ts.map +1 -1
- package/lib/main/browser/notebooks/notebooks-main.js +5 -5
- package/lib/main/browser/notebooks/notebooks-main.js.map +1 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts.map +1 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.js +3 -0
- package/lib/main/browser/notebooks/renderers/cell-output-webview.js.map +1 -1
- package/lib/main/browser/notebooks/renderers/output-webview-internal.d.ts.map +1 -1
- package/lib/main/browser/notebooks/renderers/output-webview-internal.js +4 -2
- package/lib/main/browser/notebooks/renderers/output-webview-internal.js.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.d.ts +2 -0
- package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.js +19 -1
- package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
- package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
- package/lib/main/browser/plugin-ext-frontend-module.js +3 -0
- package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
- package/lib/main/browser/plugin-icon-service.d.ts +20 -0
- package/lib/main/browser/plugin-icon-service.d.ts.map +1 -0
- package/lib/main/browser/plugin-icon-service.js +156 -0
- package/lib/main/browser/plugin-icon-service.js.map +1 -0
- package/lib/main/browser/terminal-main.d.ts +2 -2
- package/lib/main/browser/terminal-main.d.ts.map +1 -1
- package/lib/main/browser/terminal-main.js +5 -9
- package/lib/main/browser/terminal-main.js.map +1 -1
- package/lib/main/browser/test-main.d.ts +141 -0
- package/lib/main/browser/test-main.d.ts.map +1 -0
- package/lib/main/browser/test-main.js +560 -0
- package/lib/main/browser/test-main.js.map +1 -0
- package/lib/main/browser/view/plugin-view-registry.d.ts +14 -3
- package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.js +108 -56
- package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
- package/lib/main/browser/webview-views/webview-views-main.d.ts.map +1 -1
- package/lib/main/browser/webview-views/webview-views-main.js +5 -2
- package/lib/main/browser/webview-views/webview-views-main.js.map +1 -1
- package/lib/main/browser/webview-views/webview-views.d.ts +1 -0
- package/lib/main/browser/webview-views/webview-views.d.ts.map +1 -1
- package/lib/main/node/plugin-service.d.ts +2 -0
- package/lib/main/node/plugin-service.d.ts.map +1 -1
- package/lib/main/node/plugin-service.js +14 -1
- package/lib/main/node/plugin-service.js.map +1 -1
- package/lib/plugin/command-registry.d.ts +1 -3
- package/lib/plugin/command-registry.d.ts.map +1 -1
- package/lib/plugin/command-registry.js.map +1 -1
- package/lib/plugin/notebook/notebook-kernels.d.ts.map +1 -1
- package/lib/plugin/notebook/notebook-kernels.js +1 -0
- package/lib/plugin/notebook/notebook-kernels.js.map +1 -1
- package/lib/plugin/notebook/notebooks.d.ts.map +1 -1
- package/lib/plugin/notebook/notebooks.js +2 -2
- package/lib/plugin/notebook/notebooks.js.map +1 -1
- package/lib/plugin/plugin-context.d.ts.map +1 -1
- package/lib/plugin/plugin-context.js +9 -18
- package/lib/plugin/plugin-context.js.map +1 -1
- package/lib/plugin/telemetry-ext.js +1 -1
- package/lib/plugin/telemetry-ext.js.map +1 -1
- package/lib/plugin/terminal-ext.d.ts +11 -9
- package/lib/plugin/terminal-ext.d.ts.map +1 -1
- package/lib/plugin/terminal-ext.js +37 -25
- package/lib/plugin/terminal-ext.js.map +1 -1
- package/lib/plugin/test-item.d.ts +47 -0
- package/lib/plugin/test-item.d.ts.map +1 -0
- package/lib/plugin/test-item.js +196 -0
- package/lib/plugin/test-item.js.map +1 -0
- package/lib/plugin/tests.d.ts +117 -0
- package/lib/plugin/tests.d.ts.map +1 -0
- package/lib/plugin/tests.js +402 -0
- package/lib/plugin/tests.js.map +1 -0
- package/lib/plugin/tree/tree-views.d.ts.map +1 -1
- package/lib/plugin/tree/tree-views.js +2 -1
- package/lib/plugin/tree/tree-views.js.map +1 -1
- package/lib/plugin/type-converters.d.ts +10 -1
- package/lib/plugin/type-converters.d.ts.map +1 -1
- package/lib/plugin/type-converters.js +74 -2
- package/lib/plugin/type-converters.js.map +1 -1
- package/lib/plugin/types-impl.d.ts +29 -4
- package/lib/plugin/types-impl.d.ts.map +1 -1
- package/lib/plugin/types-impl.js +30 -8
- package/lib/plugin/types-impl.js.map +1 -1
- package/package.json +30 -29
- package/src/common/arrays.ts +16 -0
- package/src/common/commands.ts +19 -0
- package/src/common/plugin-api-rpc-model.ts +1 -0
- package/src/common/plugin-api-rpc.ts +69 -7
- package/src/common/plugin-protocol.ts +31 -3
- package/src/common/test-types.ts +133 -0
- package/src/hosted/browser/hosted-plugin.ts +13 -1
- package/src/hosted/node/hosted-plugin-localization-service.ts +72 -37
- package/src/hosted/node/plugin-reader.ts +4 -2
- package/src/hosted/node/scanners/scanner-theia.ts +85 -20
- package/src/main/browser/command-registry-main.ts +14 -1
- package/src/main/browser/languages-main.ts +7 -5
- package/src/main/browser/main-context.ts +4 -0
- package/src/main/browser/menus/vscode-theia-menu-mappings.ts +3 -0
- package/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +2 -2
- package/src/main/browser/notebooks/notebook-documents-main.ts +1 -1
- package/src/main/browser/notebooks/notebook-dto.ts +2 -2
- package/src/main/browser/notebooks/notebook-kernels-main.ts +6 -11
- package/src/main/browser/notebooks/notebook-renderers-main.ts +1 -1
- package/src/main/browser/notebooks/notebooks-main.ts +6 -6
- package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +3 -0
- package/src/main/browser/notebooks/renderers/output-webview-internal.ts +3 -2
- package/src/main/browser/plugin-contribution-handler.ts +19 -2
- package/src/main/browser/plugin-ext-frontend-module.ts +4 -0
- package/src/main/browser/plugin-icon-service.ts +156 -0
- package/src/main/browser/terminal-main.ts +7 -11
- package/src/main/browser/test-main.ts +618 -0
- package/src/main/browser/view/plugin-view-registry.ts +114 -56
- package/src/main/browser/webview-views/webview-views-main.ts +5 -2
- package/src/main/browser/webview-views/webview-views.ts +1 -0
- package/src/main/node/plugin-service.ts +12 -1
- package/src/plugin/command-registry.ts +1 -5
- package/src/plugin/notebook/notebook-kernels.ts +3 -1
- package/src/plugin/notebook/notebooks.ts +1 -3
- package/src/plugin/plugin-context.ts +13 -32
- package/src/plugin/telemetry-ext.ts +1 -1
- package/src/plugin/terminal-ext.ts +40 -26
- package/src/plugin/test-item.ts +174 -0
- package/src/plugin/tests.ts +482 -0
- package/src/plugin/tree/tree-views.ts +2 -1
- package/src/plugin/type-converters.ts +87 -3
- package/src/plugin/types-impl.ts +36 -5
- package/lib/plugin/stubs/tests-api.d.ts +0 -25
- package/lib/plugin/stubs/tests-api.d.ts.map +0 -1
- package/lib/plugin/stubs/tests-api.js +0 -70
- package/lib/plugin/stubs/tests-api.js.map +0 -1
- package/src/plugin/stubs/tests-api.ts +0 -102
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 Mathieu Bussieres 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 type * as theia from '@theia/plugin';
|
|
18
|
+
|
|
19
|
+
import { TreeCollection, observableProperty } from '@theia/test/lib/common/collections';
|
|
20
|
+
import { TreeDeltaBuilder } from '@theia/test/lib/common/tree-delta';
|
|
21
|
+
import { TestControllerImpl } from './tests';
|
|
22
|
+
|
|
23
|
+
export class TestTagImpl {
|
|
24
|
+
constructor(readonly id: string) { }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class TestItemImpl implements theia.TestItem {
|
|
28
|
+
constructor(readonly id: string, readonly uri: theia.Uri | undefined, label: string) {
|
|
29
|
+
this.label = label;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
protected notifyPropertyChange(property: keyof TestItemImpl, value: any): void {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
const val: any = {};
|
|
36
|
+
val[property] = value;
|
|
37
|
+
if (this.path) {
|
|
38
|
+
this.deltaBuilder?.reportChanged(this.path, val);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_deltaBuilder: TreeDeltaBuilder<string, TestItemImpl> | undefined;
|
|
43
|
+
get deltaBuilder(): TreeDeltaBuilder<string, TestItemImpl> | undefined {
|
|
44
|
+
if (this._deltaBuilder) {
|
|
45
|
+
return this._deltaBuilder;
|
|
46
|
+
} else if (this.parent) {
|
|
47
|
+
this._deltaBuilder = this.parent._deltaBuilder;
|
|
48
|
+
return this._deltaBuilder;
|
|
49
|
+
} else {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_path: string[] | undefined;
|
|
55
|
+
|
|
56
|
+
get path(): string[] {
|
|
57
|
+
if (this._path) {
|
|
58
|
+
return this._path;
|
|
59
|
+
} else if (this.parent && this.parent.path) {
|
|
60
|
+
this._path = [...this.parent.path, this.id];
|
|
61
|
+
return this._path;
|
|
62
|
+
} else {
|
|
63
|
+
return [this.id];
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
private _parent?: TestItemImpl | TestControllerImpl;
|
|
68
|
+
get realParent(): TestItemImpl | TestControllerImpl | undefined {
|
|
69
|
+
return this._parent;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
set realParent(v: TestItemImpl | TestControllerImpl | undefined) {
|
|
73
|
+
this.iterate(item => {
|
|
74
|
+
item._path = undefined;
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
this._parent = v;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get parent(): TestItemImpl | undefined {
|
|
81
|
+
const p = this.realParent;
|
|
82
|
+
if (p instanceof TestControllerImpl) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return p;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected iterate(toDo: (v: TestItemImpl) => boolean): boolean {
|
|
89
|
+
if (toDo(this)) {
|
|
90
|
+
for (const tuple of this.children) {
|
|
91
|
+
const child: TestItemImpl = tuple[1] as TestItemImpl;
|
|
92
|
+
if (!child.iterate(toDo)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
} else {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
children: TestItemCollection = new TestItemCollection(this, (v: TestItemImpl) => v.path, (v: TestItemImpl) => v.deltaBuilder);
|
|
102
|
+
@observableProperty('notifyPropertyChange')
|
|
103
|
+
tags: readonly theia.TestTag[] = [];
|
|
104
|
+
|
|
105
|
+
@observableProperty('notifyPropertyChange')
|
|
106
|
+
canResolveChildren: boolean = false;
|
|
107
|
+
@observableProperty('notifyPropertyChange')
|
|
108
|
+
busy: boolean = false;
|
|
109
|
+
@observableProperty('notifyPropertyChange')
|
|
110
|
+
label: string = '';
|
|
111
|
+
@observableProperty('notifyPropertyChange')
|
|
112
|
+
description?: string | undefined;
|
|
113
|
+
@observableProperty('notifyPropertyChange')
|
|
114
|
+
sortText?: string | undefined;
|
|
115
|
+
@observableProperty('notifyPropertyChange')
|
|
116
|
+
range: theia.Range | undefined;
|
|
117
|
+
@observableProperty('notifyPropertyChange')
|
|
118
|
+
error: string | theia.MarkdownString | undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export class TestItemCollection implements theia.TestItemCollection {
|
|
122
|
+
|
|
123
|
+
constructor(private owner: TestItemImpl | TestControllerImpl,
|
|
124
|
+
protected readonly pathOf: (v: TestItemImpl) => string[],
|
|
125
|
+
protected readonly deltaBuilder: (v: TestItemImpl | TestControllerImpl | undefined) => TreeDeltaBuilder<string, TestItemImpl> | undefined) {
|
|
126
|
+
this.values = new TreeCollection<string, TestItemImpl, TestItemImpl | TestControllerImpl>(owner, pathOf, deltaBuilder);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private readonly values: TreeCollection<string, TestItemImpl, TestItemImpl | TestControllerImpl>;
|
|
130
|
+
|
|
131
|
+
get size(): number {
|
|
132
|
+
return this.values.size;
|
|
133
|
+
}
|
|
134
|
+
replace(items: readonly theia.TestItem[]): void {
|
|
135
|
+
const toRemove = this.values.values.map(item => item.id);
|
|
136
|
+
items.forEach(item => this.add(item));
|
|
137
|
+
toRemove.forEach(key => this.delete(key));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
forEach(callback: (item: theia.TestItem, collection: theia.TestItemCollection) => unknown, thisArg?: unknown): void {
|
|
141
|
+
this.values.values.forEach(item => callback(item, this), thisArg);
|
|
142
|
+
}
|
|
143
|
+
add(item: theia.TestItem): void {
|
|
144
|
+
if (!(item instanceof TestItemImpl)) {
|
|
145
|
+
throw new Error('Not an instance of TestItem');
|
|
146
|
+
}
|
|
147
|
+
item.realParent = this.owner;
|
|
148
|
+
item._deltaBuilder = this.deltaBuilder(this.owner);
|
|
149
|
+
this.values.add(item);
|
|
150
|
+
}
|
|
151
|
+
delete(itemId: string): void {
|
|
152
|
+
this.values.remove(itemId);
|
|
153
|
+
}
|
|
154
|
+
get(itemId: string): theia.TestItem | undefined {
|
|
155
|
+
return this.values.get(itemId);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
[Symbol.iterator](): Iterator<[id: string, testItem: theia.TestItem], unknown, undefined> {
|
|
159
|
+
return this.values.entries();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
find(path: string[]): theia.TestItem | undefined {
|
|
163
|
+
let currentCollection: theia.TestItemCollection = this;
|
|
164
|
+
let item;
|
|
165
|
+
for (let i = 0; i < path.length; i++) {
|
|
166
|
+
item = currentCollection.get(path[i]);
|
|
167
|
+
if (!item) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
currentCollection = item.children;
|
|
171
|
+
}
|
|
172
|
+
return item;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 Mathieu Bussieres 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
|
+
/*---------------------------------------------------------------------------------------------
|
|
18
|
+
* Copyright (c) Microsoft Corporation and others. All rights reserved.
|
|
19
|
+
* Licensed under the MIT License. See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information.
|
|
20
|
+
*--------------------------------------------------------------------------------------------*/
|
|
21
|
+
|
|
22
|
+
// some code copied and modified from https://github.com/microsoft/vscode/blob/1.72.2/src/vs/workbench/api/common/extHostTesting.ts
|
|
23
|
+
|
|
24
|
+
// /* eslint-disable */
|
|
25
|
+
|
|
26
|
+
/* tslint:disable:typedef */
|
|
27
|
+
|
|
28
|
+
import * as theia from '@theia/plugin';
|
|
29
|
+
import { CancellationToken, CancellationTokenSource } from '@theia/core/lib/common/cancellation';
|
|
30
|
+
import { Disposable, DisposableCollection, Emitter, Event } from '@theia/core';
|
|
31
|
+
import { hash } from '@theia/core/lib/common/hash';
|
|
32
|
+
|
|
33
|
+
import { isDefined } from '@theia/core/lib/common/types';
|
|
34
|
+
import { TestingExt, PLUGIN_RPC_CONTEXT, TestingMain } from '../common/plugin-api-rpc';
|
|
35
|
+
import { CommandRegistryImpl } from './command-registry';
|
|
36
|
+
import { RPCProtocol } from '../common/rpc-protocol';
|
|
37
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
38
|
+
import * as Convert from './type-converters';
|
|
39
|
+
import { TestItemImpl, TestItemCollection } from './test-item';
|
|
40
|
+
import { AccumulatingTreeDeltaEmitter, TreeDelta } from '@theia/test/lib/common/tree-delta';
|
|
41
|
+
import {
|
|
42
|
+
TestItemDTO, TestOutputDTO, TestExecutionState, TestRunProfileDTO,
|
|
43
|
+
TestRunProfileKind, TestRunRequestDTO, TestStateChangeDTO, TestItemReference
|
|
44
|
+
} from '../common/test-types';
|
|
45
|
+
import { ChangeBatcher, observableProperty } from '@theia/test/lib/common/collections';
|
|
46
|
+
import { TestRunRequest } from './types-impl';
|
|
47
|
+
|
|
48
|
+
type RefreshHandler = (token: theia.CancellationToken) => void | theia.Thenable<void>;
|
|
49
|
+
type ResolveHandler = (item: theia.TestItem | undefined) => theia.Thenable<void> | void;
|
|
50
|
+
|
|
51
|
+
export class TestControllerImpl implements theia.TestController {
|
|
52
|
+
protected readonly _profiles = new Map<number, TestRunProfile>();
|
|
53
|
+
private activeRuns = new Map<theia.TestRunRequest, TestRun>();
|
|
54
|
+
private deltaBuilder: AccumulatingTreeDeltaEmitter<string, TestItemImpl>;
|
|
55
|
+
private _refreshHandler?: RefreshHandler;
|
|
56
|
+
private _resolveHandler?: ResolveHandler;
|
|
57
|
+
|
|
58
|
+
constructor(
|
|
59
|
+
private onDispose: () => void,
|
|
60
|
+
protected readonly proxy: TestingMain,
|
|
61
|
+
readonly id: string,
|
|
62
|
+
private _label: string) {
|
|
63
|
+
|
|
64
|
+
this.proxy.$registerTestController(id, _label);
|
|
65
|
+
|
|
66
|
+
this.deltaBuilder = new AccumulatingTreeDeltaEmitter<string, TestItemImpl>(200);
|
|
67
|
+
this.deltaBuilder.onDidFlush(delta => {
|
|
68
|
+
// console.debug('flushing delta'); // logging levels don't work in plugin host: https://github.com/eclipse-theia/theia/issues/12234
|
|
69
|
+
const mapped = this.mapDeltas(delta);
|
|
70
|
+
// console.debug(JSON.stringify(mapped, undefined, 3));
|
|
71
|
+
this.proxy.$notifyDelta(id, mapped);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
this.items = new TestItemCollection(this, item => item.path, () => this.deltaBuilder);
|
|
75
|
+
}
|
|
76
|
+
mapDeltas(deltas: TreeDelta<string, TestItemImpl>[]): TreeDelta<string, TestItemDTO>[] {
|
|
77
|
+
return deltas.map(delta => this.mapDelta(delta));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
mapDelta(delta: TreeDelta<string, TestItemImpl>): TreeDelta<string, TestItemDTO> {
|
|
81
|
+
return {
|
|
82
|
+
path: delta.path,
|
|
83
|
+
type: delta.type,
|
|
84
|
+
value: delta.value ? Convert.TestItem.fromPartial(delta.value) : undefined,
|
|
85
|
+
childDeltas: delta.childDeltas?.map(d => this.mapDelta(d))
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
readonly items: TestItemCollection;
|
|
90
|
+
|
|
91
|
+
get label(): string {
|
|
92
|
+
return this._label;
|
|
93
|
+
}
|
|
94
|
+
set label(value: string) {
|
|
95
|
+
this._label = value;
|
|
96
|
+
this.proxy.$updateController(this.id, { label: value });
|
|
97
|
+
}
|
|
98
|
+
get refreshHandler(): RefreshHandler | undefined {
|
|
99
|
+
return this._refreshHandler;
|
|
100
|
+
}
|
|
101
|
+
set refreshHandler(value: RefreshHandler | undefined) {
|
|
102
|
+
this._refreshHandler = value;
|
|
103
|
+
this.proxy.$updateController(this.id, { canRefresh: !!value });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get resolveHandler(): ResolveHandler | undefined {
|
|
107
|
+
return this._resolveHandler;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
set resolveHandler(handler: ResolveHandler | undefined) {
|
|
111
|
+
this._resolveHandler = handler;
|
|
112
|
+
this.proxy.$updateController(this.id, { canResolve: !!handler });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getProfile(id: string) {
|
|
116
|
+
return this._profiles.get(Number.parseInt(id));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
createRunProfile(label: string, kind: TestRunProfileKind, runHandler: (request: theia.TestRunRequest, token: CancellationToken) => Thenable<void> | void,
|
|
120
|
+
isDefault?: boolean, tag?: theia.TestTag, supportsContinuousRun?: boolean): theia.TestRunProfile {
|
|
121
|
+
// Derive the profile ID from a hash so that the same profile will tend
|
|
122
|
+
// to have the same hashes, allowing re-run requests to work across reloads.
|
|
123
|
+
let profileId = hash(label);
|
|
124
|
+
while (this._profiles.has(profileId)) {
|
|
125
|
+
profileId++;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const profile = new TestRunProfile(this.proxy, this.id, profileId.toString(), label, kind, runHandler, isDefault, tag);
|
|
129
|
+
this._profiles.set(profileId, profile);
|
|
130
|
+
return profile;
|
|
131
|
+
}
|
|
132
|
+
createTestItem(id: string, label: string, uri?: theia.Uri): theia.TestItem {
|
|
133
|
+
return new TestItemImpl(id, uri, label);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
createTestRun(request: theia.TestRunRequest, name?: string, persist: boolean = true): theia.TestRun {
|
|
137
|
+
return this.testRunStarted(request, name || '', persist, true);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
dispose() {
|
|
141
|
+
this.proxy.$unregisterTestController(this.id);
|
|
142
|
+
this.onDispose();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
protected testRunStarted(request: theia.TestRunRequest, name: string, persist: boolean, isRunning: boolean): TestRun {
|
|
146
|
+
const existing = this.activeRuns.get(request);
|
|
147
|
+
if (existing) {
|
|
148
|
+
return existing;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const run = new TestRun(this, this.proxy, name, persist, isRunning);
|
|
152
|
+
const endListener = run.onWillFlush(() => {
|
|
153
|
+
// make sure we notify the front end of test item changes before test run state is sent
|
|
154
|
+
this.deltaBuilder.flush();
|
|
155
|
+
});
|
|
156
|
+
run.onDidEnd(() => {
|
|
157
|
+
endListener.dispose();
|
|
158
|
+
this.activeRuns.delete(request);
|
|
159
|
+
});
|
|
160
|
+
this.activeRuns.set(request, run);
|
|
161
|
+
return run;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
runTestsForUI(profileId: string, name: string, includedTests: string[][], excludedTests: string[][]): void {
|
|
165
|
+
const profile = this.getProfile(profileId);
|
|
166
|
+
if (!profile) {
|
|
167
|
+
console.error(`No test run profile found for controller ${this.id} with id ${profileId} `);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const includeTests = includedTests
|
|
172
|
+
.map(testId => this.items.find(testId))
|
|
173
|
+
.filter(isDefined);
|
|
174
|
+
|
|
175
|
+
if (includeTests.length === 0) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
function isPrefix(left: string[], right: string[]) {
|
|
179
|
+
if (left.length > right.length) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (let i = 0; i < left.length; i++) {
|
|
184
|
+
if (left[i] !== right[i]) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const excludeTests = excludedTests
|
|
192
|
+
.filter(path => includedTests.some(
|
|
193
|
+
includedPath => isPrefix(path, includedPath)
|
|
194
|
+
))
|
|
195
|
+
.map(path => this.items.find(path))
|
|
196
|
+
.filter(isDefined);
|
|
197
|
+
|
|
198
|
+
const request = new TestRunRequest(
|
|
199
|
+
includeTests, excludeTests, profile, false // don't support continuous run yet
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const run = this.testRunStarted(request, name, false, false);
|
|
203
|
+
profile.runHandler(request, run.token);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
cancelRun(runId?: string): void {
|
|
207
|
+
if (runId === undefined) {
|
|
208
|
+
this.activeRuns.forEach(run => run.cancel());
|
|
209
|
+
} else {
|
|
210
|
+
const run = [...this.activeRuns.values()].find(r => r.id === runId);
|
|
211
|
+
if (!run) {
|
|
212
|
+
throw new Error(`TestController ${this.id} cannot cancel non - existing run ${runId} `);
|
|
213
|
+
}
|
|
214
|
+
run.cancel();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
invalidateTestResults(items?: theia.TestItem | readonly theia.TestItem[] | undefined): void {
|
|
219
|
+
// do nothing for the moment, since we don't have a UI to "mark as outdated and deprioritized in the editor's UI."
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function checkTestInstance(item: theia.TestItem): TestItemImpl;
|
|
224
|
+
function checkTestInstance(item?: theia.TestItem): TestItemImpl | undefined;
|
|
225
|
+
function checkTestInstance(item?: theia.TestItem): TestItemImpl | undefined {
|
|
226
|
+
if (item instanceof TestItemImpl) {
|
|
227
|
+
if (!item.path) {
|
|
228
|
+
throw new Error('Test item not added to a collection');
|
|
229
|
+
}
|
|
230
|
+
return <TestItemImpl>item;
|
|
231
|
+
} else if (item) {
|
|
232
|
+
throw new Error('Not a TestItem instance');
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
class TestRun implements theia.TestRun {
|
|
238
|
+
private onDidEndEmitter = new Emitter<void>();
|
|
239
|
+
onDidEnd: Event<void> = this.onDidEndEmitter.event;
|
|
240
|
+
private onWillFlushEmitter = new Emitter<void>();
|
|
241
|
+
onWillFlush: Event<void> = this.onWillFlushEmitter.event;
|
|
242
|
+
|
|
243
|
+
readonly id: string;
|
|
244
|
+
private testStateDeltas = new Map<theia.TestItem, TestStateChangeDTO>();
|
|
245
|
+
private testOutputDeltas: TestOutputDTO[] = [];
|
|
246
|
+
private changeBatcher = new ChangeBatcher(() => {
|
|
247
|
+
this.emitChange();
|
|
248
|
+
}, 200);
|
|
249
|
+
private ended: boolean;
|
|
250
|
+
private tokenSource: CancellationTokenSource;
|
|
251
|
+
readonly token: CancellationToken;
|
|
252
|
+
|
|
253
|
+
constructor(
|
|
254
|
+
private readonly controller: TestControllerImpl,
|
|
255
|
+
private readonly proxy: TestingMain,
|
|
256
|
+
readonly name: string,
|
|
257
|
+
readonly isPersisted: boolean,
|
|
258
|
+
isRunning: boolean) {
|
|
259
|
+
this.id = uuidv4();
|
|
260
|
+
|
|
261
|
+
this.tokenSource = new CancellationTokenSource();
|
|
262
|
+
this.token = this.tokenSource.token;
|
|
263
|
+
|
|
264
|
+
this.proxy.$notifyTestRunCreated(this.controller.id, { id: this.id, name: this.name, isRunning });
|
|
265
|
+
}
|
|
266
|
+
enqueued(test: theia.TestItem): void {
|
|
267
|
+
this.updateTestState(test, { itemPath: checkTestInstance(test).path, state: TestExecutionState.Queued });
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
started(test: theia.TestItem): void {
|
|
271
|
+
this.updateTestState(test, { itemPath: checkTestInstance(test).path, state: TestExecutionState.Running });
|
|
272
|
+
}
|
|
273
|
+
skipped(test: theia.TestItem): void {
|
|
274
|
+
this.updateTestState(test, { itemPath: checkTestInstance(test).path, state: TestExecutionState.Skipped });
|
|
275
|
+
}
|
|
276
|
+
failed(test: theia.TestItem, message: theia.TestMessage | readonly theia.TestMessage[], duration?: number | undefined): void {
|
|
277
|
+
this.updateTestState(test, { itemPath: checkTestInstance(test).path, state: TestExecutionState.Failed, messages: Convert.TestMessage.from(message), duration });
|
|
278
|
+
}
|
|
279
|
+
errored(test: theia.TestItem, message: theia.TestMessage | readonly theia.TestMessage[], duration?: number | undefined): void {
|
|
280
|
+
this.updateTestState(test, { itemPath: checkTestInstance(test).path, state: TestExecutionState.Errored, messages: Convert.TestMessage.from(message), duration });
|
|
281
|
+
}
|
|
282
|
+
passed(test: theia.TestItem, duration?: number | undefined): void {
|
|
283
|
+
this.updateTestState(test, { itemPath: checkTestInstance(test).path, state: TestExecutionState.Passed, duration });
|
|
284
|
+
}
|
|
285
|
+
appendOutput(output: string, location?: theia.Location | undefined, test?: theia.TestItem | undefined): void {
|
|
286
|
+
this.testOutputDeltas.push({ output, location: Convert.fromLocation(location), itemPath: checkTestInstance(test)?.path });
|
|
287
|
+
this.changeBatcher.changeOccurred();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
end(): void {
|
|
291
|
+
this.ended = true;
|
|
292
|
+
this.proxy.$notifyTestRunEnded(this.controller.id, this.id);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private checkNotEnded(test: theia.TestItem): boolean {
|
|
296
|
+
if (this.ended) {
|
|
297
|
+
console.warn(`Setting the state of test "${test.id}" is a no - op after the run ends.`);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private updateTestState<T extends TestStateChangeDTO>(item: theia.TestItem, state: T) {
|
|
304
|
+
if (this.checkNotEnded(item)) {
|
|
305
|
+
this.testStateDeltas.set(item, state);
|
|
306
|
+
this.changeBatcher.changeOccurred();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
emitChange() {
|
|
311
|
+
this.onWillFlushEmitter.fire();
|
|
312
|
+
this.proxy.$notifyTestStateChanged(this.controller.id, this.id, [...this.testStateDeltas.values()], this.testOutputDeltas);
|
|
313
|
+
this.testOutputDeltas = [];
|
|
314
|
+
this.testStateDeltas = new Map();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
cancel() {
|
|
318
|
+
this.tokenSource.cancel();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export class TestingExtImpl implements TestingExt {
|
|
323
|
+
private readonly controllersById = new Map<string, TestControllerImpl>();
|
|
324
|
+
private readonly proxy: TestingMain;
|
|
325
|
+
|
|
326
|
+
constructor(
|
|
327
|
+
rpc: RPCProtocol,
|
|
328
|
+
commands: CommandRegistryImpl
|
|
329
|
+
) {
|
|
330
|
+
this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.TESTING_MAIN);
|
|
331
|
+
|
|
332
|
+
commands.registerArgumentProcessor({
|
|
333
|
+
processArgument: arg => {
|
|
334
|
+
if (TestItemReference.is(arg)) {
|
|
335
|
+
return this.toTestItem(arg);
|
|
336
|
+
} else if (Array.isArray(arg)) {
|
|
337
|
+
return arg.map(param => TestItemReference.is(param) ? this.toTestItem(param) : param);
|
|
338
|
+
} else {
|
|
339
|
+
return arg;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
toTestItem(ref: TestItemReference): theia.TestItem {
|
|
347
|
+
const result = this.withController(ref.controllerId).items.find(ref.testPath);
|
|
348
|
+
if (!result) {
|
|
349
|
+
throw new Error(`Test item for controller ${ref.controllerId} not found: ${ref.testPath}`);
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
protected withController(controllerId: string): TestControllerImpl {
|
|
355
|
+
const controller = this.controllersById.get(controllerId);
|
|
356
|
+
if (!controller) {
|
|
357
|
+
throw new Error(`No test controller found with id "${controllerId}"`);
|
|
358
|
+
}
|
|
359
|
+
return controller;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
$onResolveChildren(controllerId: string, path: string[]): void {
|
|
363
|
+
const controller = this.withController(controllerId);
|
|
364
|
+
if (controller.resolveHandler) {
|
|
365
|
+
const item = controller.items.find(path);
|
|
366
|
+
if (item?.canResolveChildren) { // the item and resolve handler might have been been changed, but not sent to the front end
|
|
367
|
+
controller.resolveHandler?.(item);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Implements theia.test.registerTestProvider
|
|
374
|
+
*/
|
|
375
|
+
createTestController(controllerId: string, label: string): theia.TestController {
|
|
376
|
+
if (this.controllersById.has(controllerId)) {
|
|
377
|
+
throw new Error(`Attempt to insert a duplicate controller with ID "${controllerId}"`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const disposable = new DisposableCollection();
|
|
381
|
+
|
|
382
|
+
const controller = new TestControllerImpl(() => disposable.dispose(), this.proxy, controllerId, label);
|
|
383
|
+
|
|
384
|
+
this.controllersById.set(controllerId, controller);
|
|
385
|
+
disposable.push(Disposable.create(() => this.controllersById.delete(controllerId)));
|
|
386
|
+
|
|
387
|
+
return controller;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** @inheritdoc */
|
|
391
|
+
$onConfigureRunProfile(controllerId: string, profileId: string): void {
|
|
392
|
+
this.controllersById.get(controllerId)?.getProfile(profileId)?.configureHandler?.();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** @inheritdoc */
|
|
396
|
+
async $refreshTests(controllerId: string, token: CancellationToken): Promise<void> {
|
|
397
|
+
await this.withController(controllerId).refreshHandler?.(token);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Runs tests with the given set of IDs. Allows for test from multiple
|
|
402
|
+
* providers to be run.
|
|
403
|
+
* @override
|
|
404
|
+
*/
|
|
405
|
+
$onRunControllerTests(reqs: TestRunRequestDTO[]): void {
|
|
406
|
+
reqs.map(req => this.runTestsForUI(req));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
runTestsForUI(req: TestRunRequestDTO): void {
|
|
410
|
+
this.withController(req.controllerId).runTestsForUI(req.profileId, req.name, req.includedTests, req.excludedTests);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Cancels an ongoing test run.
|
|
415
|
+
*/
|
|
416
|
+
$onCancelTestRun(controllerId: string, runId: string): void {
|
|
417
|
+
this.withController(controllerId).cancelRun(runId);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export class TestRunProfile implements theia.TestRunProfile {
|
|
422
|
+
private readonly proxy: TestingMain;
|
|
423
|
+
supportsContinuousRun: boolean = false;
|
|
424
|
+
|
|
425
|
+
constructor(
|
|
426
|
+
proxy: TestingMain,
|
|
427
|
+
readonly controllerId: string,
|
|
428
|
+
readonly profileId: string,
|
|
429
|
+
label: string,
|
|
430
|
+
readonly kind: theia.TestRunProfileKind,
|
|
431
|
+
public runHandler: (request: theia.TestRunRequest, token: theia.CancellationToken) => Thenable<void> | void,
|
|
432
|
+
isDefault = false,
|
|
433
|
+
tag: theia.TestTag | undefined = undefined,
|
|
434
|
+
) {
|
|
435
|
+
this.proxy = proxy;
|
|
436
|
+
this.label = label;
|
|
437
|
+
this.tag = tag;
|
|
438
|
+
this.label = label;
|
|
439
|
+
this.isDefault = isDefault;
|
|
440
|
+
|
|
441
|
+
this.proxy.$notifyTestRunProfileCreated(controllerId, {
|
|
442
|
+
id: profileId,
|
|
443
|
+
kind: kind,
|
|
444
|
+
tag: tag ? tag.toString() : '',
|
|
445
|
+
label: label,
|
|
446
|
+
isDefault: isDefault,
|
|
447
|
+
canConfigure: false,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
452
|
+
protected notifyPropertyChange(property: keyof TestRunProfileDTO, value: any): void {
|
|
453
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
454
|
+
const val: any = {};
|
|
455
|
+
val[property] = value;
|
|
456
|
+
this.proxy.$updateTestRunProfile(this.controllerId, this.profileId, val);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
@observableProperty('notifyPropertyChange')
|
|
460
|
+
label: string;
|
|
461
|
+
|
|
462
|
+
@observableProperty('notifyPropertyChange')
|
|
463
|
+
isDefault: boolean;
|
|
464
|
+
|
|
465
|
+
@observableProperty('notifyTagChange')
|
|
466
|
+
tag: theia.TestTag | undefined;
|
|
467
|
+
|
|
468
|
+
protected notifyTagChange(_property: keyof TestRunProfileDTO, value?: theia.TestTag): void {
|
|
469
|
+
this.proxy.$updateTestRunProfile(this.controllerId, this.profileId, { tag: value ? value.toString() : '' });
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
@observableProperty('notifyConfigureHandlerChange')
|
|
473
|
+
configureHandler: () => void | undefined;
|
|
474
|
+
|
|
475
|
+
protected notifyConfigureHandlerChange(_property: keyof TestRunProfileDTO, value?: () => void): void {
|
|
476
|
+
this.proxy.$updateTestRunProfile(this.controllerId, this.profileId, { canConfigure: !!value });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
dispose(): void {
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
}
|
|
@@ -34,6 +34,7 @@ import { PluginIconPath } from '../plugin-icon-path';
|
|
|
34
34
|
import { URI } from '@theia/core/shared/vscode-uri';
|
|
35
35
|
import { UriComponents } from '@theia/core/lib/common/uri';
|
|
36
36
|
import { isObject } from '@theia/core';
|
|
37
|
+
import { coalesce } from '../../common/arrays';
|
|
37
38
|
|
|
38
39
|
export class TreeViewsExtImpl implements TreeViewsExt {
|
|
39
40
|
private proxy: TreeViewsMain;
|
|
@@ -407,7 +408,7 @@ class TreeViewExtImpl<T> implements Disposable {
|
|
|
407
408
|
// ask data provider for children for cached element
|
|
408
409
|
const result = await this.options.treeDataProvider.getChildren(parent);
|
|
409
410
|
if (result) {
|
|
410
|
-
const treeItemPromises = result.map(async value => {
|
|
411
|
+
const treeItemPromises = coalesce(result).map(async value => {
|
|
411
412
|
|
|
412
413
|
// Ask data provider for a tree item for the value
|
|
413
414
|
// Data provider must return theia.TreeItem
|