@theia/test 1.45.1 → 1.46.0-next.72

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.
Files changed (53) hide show
  1. package/lib/browser/constants.d.ts +45 -45
  2. package/lib/browser/constants.js +17 -17
  3. package/lib/browser/test-service.d.ts +154 -154
  4. package/lib/browser/test-service.js +247 -247
  5. package/lib/browser/view/test-context-key-service.d.ts +7 -7
  6. package/lib/browser/view/test-context-key-service.js +51 -51
  7. package/lib/browser/view/test-execution-state-manager.d.ts +13 -13
  8. package/lib/browser/view/test-execution-state-manager.js +173 -173
  9. package/lib/browser/view/test-output-ui-model.d.ts +47 -47
  10. package/lib/browser/view/test-output-ui-model.js +151 -151
  11. package/lib/browser/view/test-output-view-contribution.d.ts +5 -5
  12. package/lib/browser/view/test-output-view-contribution.js +47 -47
  13. package/lib/browser/view/test-output-widget.d.ts +24 -24
  14. package/lib/browser/view/test-output-widget.js +158 -158
  15. package/lib/browser/view/test-result-view-contribution.d.ts +5 -5
  16. package/lib/browser/view/test-result-view-contribution.js +47 -47
  17. package/lib/browser/view/test-result-widget.d.ts +20 -20
  18. package/lib/browser/view/test-result-widget.js +108 -108
  19. package/lib/browser/view/test-run-view-contribution.d.ts +17 -17
  20. package/lib/browser/view/test-run-view-contribution.js +100 -100
  21. package/lib/browser/view/test-run-widget.d.ts +58 -58
  22. package/lib/browser/view/test-run-widget.js +310 -310
  23. package/lib/browser/view/test-tree-widget.d.ts +67 -67
  24. package/lib/browser/view/test-tree-widget.js +386 -386
  25. package/lib/browser/view/test-view-contribution.d.ts +45 -45
  26. package/lib/browser/view/test-view-contribution.js +288 -288
  27. package/lib/browser/view/test-view-frontend-module.d.ts +6 -6
  28. package/lib/browser/view/test-view-frontend-module.js +121 -121
  29. package/lib/common/collections.d.ts +46 -46
  30. package/lib/common/collections.js +210 -210
  31. package/lib/common/tree-delta.d.ts +51 -51
  32. package/lib/common/tree-delta.js +240 -240
  33. package/lib/common/tree-delta.spec.d.ts +1 -1
  34. package/lib/common/tree-delta.spec.js +139 -139
  35. package/package.json +8 -8
  36. package/src/browser/constants.ts +71 -71
  37. package/src/browser/style/index.css +41 -41
  38. package/src/browser/test-service.ts +355 -355
  39. package/src/browser/view/test-context-key-service.ts +36 -36
  40. package/src/browser/view/test-execution-state-manager.ts +147 -147
  41. package/src/browser/view/test-output-ui-model.ts +156 -156
  42. package/src/browser/view/test-output-view-contribution.ts +34 -34
  43. package/src/browser/view/test-output-widget.ts +148 -148
  44. package/src/browser/view/test-result-view-contribution.ts +34 -34
  45. package/src/browser/view/test-result-widget.ts +92 -92
  46. package/src/browser/view/test-run-view-contribution.ts +89 -89
  47. package/src/browser/view/test-run-widget.tsx +271 -271
  48. package/src/browser/view/test-tree-widget.tsx +360 -360
  49. package/src/browser/view/test-view-contribution.ts +300 -300
  50. package/src/browser/view/test-view-frontend-module.ts +134 -134
  51. package/src/common/collections.ts +223 -223
  52. package/src/common/tree-delta.spec.ts +166 -166
  53. package/src/common/tree-delta.ts +259 -259
@@ -1,355 +1,355 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2022 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 { CancellationToken, ContributionProvider, Disposable, Emitter, Event, QuickPickService, isObject, nls } from '@theia/core/lib/common';
18
- import { CancellationTokenSource, Location, Range } from '@theia/core/shared/vscode-languageserver-protocol';
19
- import { CollectionDelta, TreeDelta } from '../common/tree-delta';
20
- import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
21
- import URI from '@theia/core/lib/common/uri';
22
- import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
23
- import { groupBy } from '../common/collections';
24
- import { codiconArray } from '@theia/core/lib/browser';
25
-
26
- export enum TestRunProfileKind {
27
- Run = 1,
28
- Debug = 2,
29
- Coverage = 3
30
- }
31
-
32
- export interface TestRunProfile {
33
- readonly kind: TestRunProfileKind;
34
- readonly label: string,
35
- readonly isDefault: boolean;
36
- readonly canConfigure: boolean;
37
- readonly tag: string;
38
- run(name: string, included: readonly TestItem[], excluded: readonly TestItem[]): void;
39
- configure(): void;
40
- }
41
-
42
- export interface TestOutputItem {
43
- readonly output: string;
44
- readonly location?: Location;
45
- }
46
-
47
- export enum TestExecutionState {
48
- Queued = 1,
49
- Running = 2,
50
- Passed = 3,
51
- Failed = 4,
52
- Skipped = 5,
53
- Errored = 6
54
- }
55
-
56
- export interface TestMessage {
57
- readonly expected?: string;
58
- readonly actual?: string;
59
- readonly location: Location;
60
- readonly message: string | MarkdownString;
61
- readonly contextValue?: string;
62
- }
63
-
64
- export namespace TestMessage {
65
- export function is(obj: unknown): obj is TestMessage {
66
- return isObject<TestMessage>(obj) && (MarkdownString.is(obj.message) || typeof obj.message === 'string');
67
- }
68
- }
69
-
70
- export interface TestState {
71
- readonly state: TestExecutionState;
72
- }
73
-
74
- export interface TestFailure extends TestState {
75
- readonly state: TestExecutionState.Failed | TestExecutionState.Errored;
76
- readonly messages: TestMessage[];
77
- readonly duration?: number;
78
- }
79
-
80
- export namespace TestFailure {
81
- export function is(obj: unknown): obj is TestFailure {
82
- return isObject<TestFailure>(obj) && (obj.state === TestExecutionState.Failed || obj.state === TestExecutionState.Errored) && Array.isArray(obj.messages);
83
- }
84
- }
85
-
86
- export interface TestSuccess extends TestState {
87
- readonly state: TestExecutionState.Passed;
88
- readonly duration?: number;
89
- }
90
-
91
- export interface TestStateChangedEvent {
92
- test: TestItem;
93
- oldState: TestState | undefined;
94
- newState: TestState | undefined;
95
- }
96
-
97
- export interface TestRun {
98
- cancel(): void;
99
- readonly name: string;
100
- readonly isRunning: boolean;
101
- readonly controller: TestController;
102
-
103
- onDidChangeProperty: Event<{ name?: string, isRunning?: boolean }>;
104
-
105
- getTestState(item: TestItem): TestState | undefined;
106
- onDidChangeTestState: Event<TestStateChangedEvent[]>;
107
-
108
- getOutput(item?: TestItem): readonly TestOutputItem[];
109
- onDidChangeTestOutput: Event<[TestItem | undefined, TestOutputItem][]>;
110
-
111
- readonly items: readonly TestItem[];
112
- }
113
-
114
- export namespace TestRun {
115
- export function is(obj: unknown): obj is TestRun {
116
- return isObject<TestRun>(obj)
117
- && typeof obj.cancel === 'function'
118
- && typeof obj.name === 'string'
119
- && typeof obj.isRunning === 'boolean'
120
- && typeof obj.controller === 'object'
121
- && typeof obj.onDidChangeProperty === 'function'
122
- && typeof obj.getTestState === 'function'
123
- && typeof obj.onDidChangeTestState === 'function'
124
- && typeof obj.onDidChangeTestState === 'function'
125
- && typeof obj.getOutput === 'function'
126
- && typeof obj.onDidChangeTestOutput === 'function'
127
- && Array.isArray(obj.items);
128
- }
129
- }
130
-
131
- export interface TestItem {
132
- readonly id: string;
133
- readonly label: string;
134
- readonly range?: Range;
135
- readonly sortKey?: string;
136
- readonly tags: string[];
137
- readonly uri?: URI;
138
- readonly busy: boolean;
139
- readonly tests: readonly TestItem[];
140
- readonly description?: string;
141
- readonly error?: string | MarkdownString;
142
- readonly parent: TestItem | undefined;
143
- readonly controller: TestController | undefined;
144
- readonly canResolveChildren: boolean;
145
- resolveChildren(): void;
146
- readonly path: string[];
147
- }
148
-
149
- export namespace TestItem {
150
- export function is(obj: unknown): obj is TestItem {
151
- return isObject<TestItem>(obj)
152
- && obj.id !== undefined
153
- && obj.label !== undefined
154
- && Array.isArray(obj.tags)
155
- && Array.isArray(obj.tests)
156
- && obj.busy !== undefined
157
- && obj.canResolveChildren !== undefined
158
- && typeof obj.resolveChildren === 'function';
159
- }
160
- }
161
-
162
- export interface TestController {
163
- readonly id: string;
164
- readonly label: string;
165
- readonly tests: readonly TestItem[];
166
- readonly testRunProfiles: readonly TestRunProfile[];
167
- readonly testRuns: readonly TestRun[];
168
-
169
- readonly onItemsChanged: Event<TreeDelta<string, TestItem>[]>;
170
- readonly onRunsChanged: Event<CollectionDelta<TestRun, TestRun>>;
171
- readonly onProfilesChanged: Event<CollectionDelta<TestRunProfile, TestRunProfile>>;
172
-
173
- refreshTests(token: CancellationToken): Promise<void>;
174
- clearRuns(): void;
175
- }
176
-
177
- export interface TestService {
178
- clearResults(): void;
179
- configureProfile(): void;
180
- runTestsWithProfile(tests: TestItem[]): void;
181
- runTests(profileKind: TestRunProfileKind, tests: TestItem[]): void;
182
- runAllTests(profileKind: TestRunProfileKind): void;
183
- getControllers(): TestController[];
184
- registerTestController(controller: TestController): Disposable;
185
- onControllersChanged: Event<CollectionDelta<string, TestController>>;
186
-
187
- refresh(): void;
188
- cancelRefresh(): void;
189
- isRefreshing: boolean;
190
- onDidChangeIsRefreshing: Event<void>;
191
- }
192
-
193
- export const TestContribution = Symbol('TestContribution');
194
-
195
- export interface TestContribution {
196
- registerTestControllers(service: TestService): void;
197
- }
198
-
199
- export const TestService = Symbol('TestService');
200
-
201
- @injectable()
202
- export class DefaultTestService implements TestService {
203
- @inject(QuickPickService) quickpickService: QuickPickService;
204
-
205
- private testRunCounter = 0;
206
-
207
- private onDidChangeIsRefreshingEmitter = new Emitter<void>();
208
- onDidChangeIsRefreshing: Event<void> = this.onDidChangeIsRefreshingEmitter.event;
209
-
210
- private controllers: Map<string, TestController> = new Map();
211
- private refreshing: Set<CancellationTokenSource> = new Set();
212
- private onControllersChangedEmitter = new Emitter<CollectionDelta<string, TestController>>();
213
-
214
- @inject(ContributionProvider) @named(TestContribution)
215
- protected readonly contributionProvider: ContributionProvider<TestContribution>;
216
-
217
- @postConstruct()
218
- protected registerContributions(): void {
219
- this.contributionProvider.getContributions().forEach(contribution => contribution.registerTestControllers(this));
220
- }
221
-
222
- onControllersChanged: Event<CollectionDelta<string, TestController>> = this.onControllersChangedEmitter.event;
223
-
224
- registerTestController(controller: TestController): Disposable {
225
- if (this.controllers.has(controller.id)) {
226
- throw new Error('TestController already registered: ' + controller.id);
227
- }
228
- this.controllers.set(controller.id, controller);
229
- this.onControllersChangedEmitter.fire({ added: [controller] });
230
- return Disposable.create(() => {
231
- this.controllers.delete(controller.id);
232
- this.onControllersChangedEmitter.fire({ removed: [controller.id] });
233
- });
234
- }
235
-
236
- getControllers(): TestController[] {
237
- return Array.from(this.controllers.values());
238
- }
239
-
240
- refresh(): void {
241
- const cts = new CancellationTokenSource();
242
- this.refreshing.add(cts);
243
-
244
- Promise.all(this.getControllers().map(controller => controller.refreshTests(cts.token))).then(() => {
245
- this.refreshing.delete(cts);
246
- if (this.refreshing.size === 0) {
247
- this.onDidChangeIsRefreshingEmitter.fire();
248
- }
249
- });
250
-
251
- if (this.refreshing.size === 1) {
252
- this.onDidChangeIsRefreshingEmitter.fire();
253
- }
254
- }
255
-
256
- cancelRefresh(): void {
257
- if (this.refreshing.size > 0) {
258
- this.refreshing.forEach(cts => cts.cancel());
259
- this.refreshing.clear();
260
- this.onDidChangeIsRefreshingEmitter.fire();
261
- }
262
- }
263
-
264
- get isRefreshing(): boolean {
265
- return this.refreshing.size > 0;
266
- }
267
-
268
- runAllTests(profileKind: TestRunProfileKind): void {
269
- this.getControllers().forEach(controller => {
270
- this.runTestForController(controller, profileKind, controller.tests);
271
- });
272
- }
273
-
274
- protected async runTestForController(controller: TestController, profileKind: TestRunProfileKind, items: readonly TestItem[]): Promise<void> {
275
- const runProfiles = controller.testRunProfiles.filter(profile => profile.kind === profileKind);
276
- let activeProfile;
277
- if (runProfiles.length === 1) {
278
- activeProfile = runProfiles[0];
279
- } else if (runProfiles.length > 1) {
280
- const defaultProfile = runProfiles.find(p => p.isDefault);
281
- if (defaultProfile) {
282
- activeProfile = defaultProfile;
283
- } else {
284
-
285
- activeProfile = await this.pickProfile(runProfiles, nls.localizeByDefault('Pick a test profile to use'));
286
- }
287
- }
288
- if (activeProfile) {
289
- activeProfile.run(`Test run #${this.testRunCounter++}`, items, []);
290
- }
291
- }
292
-
293
- protected async pickProfile(runProfiles: readonly TestRunProfile[], title: string): Promise<TestRunProfile | undefined> {
294
- if (runProfiles.length === 0) {
295
- return undefined;
296
- }
297
- // eslint-disable-next-line arrow-body-style
298
- const picks = runProfiles.map(profile => {
299
- let iconClasses;
300
- if (profile.kind === TestRunProfileKind.Run) {
301
- iconClasses = codiconArray('run');
302
- } else if (profile.kind === TestRunProfileKind.Debug) {
303
- iconClasses = codiconArray('debug-alt');
304
- }
305
- return {
306
- iconClasses,
307
- label: profile.label,
308
- profile: profile
309
- };
310
- });
311
-
312
- return (await this.quickpickService.show(picks, { title: title }))?.profile;
313
-
314
- }
315
-
316
- runTests(profileKind: TestRunProfileKind, items: TestItem[]): void {
317
- groupBy(items, item => item.controller).forEach((tests, controller) => {
318
- if (controller) {
319
- this.runTestForController(controller, profileKind, tests);
320
- }
321
- });
322
- }
323
-
324
- runTestsWithProfile(items: TestItem[]): void {
325
- groupBy(items, item => item.controller).forEach((tests, controller) => {
326
- if (controller) {
327
- this.pickProfile(controller.testRunProfiles, nls.localizeByDefault('Pick a test profile to use')).then(activeProfile => {
328
- if (activeProfile) {
329
- activeProfile.run(`Test run #${this.testRunCounter++}`, items, []);
330
- }
331
- });
332
- }
333
- });
334
- }
335
-
336
- configureProfile(): void {
337
- const profiles: TestRunProfile[] = [];
338
-
339
- for (const controller of this.controllers.values()) {
340
- profiles.push(...controller.testRunProfiles);
341
- }
342
- ;
343
- this.pickProfile(profiles.filter(profile => profile.canConfigure), nls.localizeByDefault('Select a profile to update')).then(profile => {
344
- if (profile) {
345
- profile.configure();
346
- }
347
- });
348
- }
349
-
350
- clearResults(): void {
351
- for (const controller of this.controllers.values()) {
352
- controller.clearRuns();
353
- }
354
- }
355
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 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 { CancellationToken, ContributionProvider, Disposable, Emitter, Event, QuickPickService, isObject, nls } from '@theia/core/lib/common';
18
+ import { CancellationTokenSource, Location, Range } from '@theia/core/shared/vscode-languageserver-protocol';
19
+ import { CollectionDelta, TreeDelta } from '../common/tree-delta';
20
+ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
21
+ import URI from '@theia/core/lib/common/uri';
22
+ import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
23
+ import { groupBy } from '../common/collections';
24
+ import { codiconArray } from '@theia/core/lib/browser';
25
+
26
+ export enum TestRunProfileKind {
27
+ Run = 1,
28
+ Debug = 2,
29
+ Coverage = 3
30
+ }
31
+
32
+ export interface TestRunProfile {
33
+ readonly kind: TestRunProfileKind;
34
+ readonly label: string,
35
+ readonly isDefault: boolean;
36
+ readonly canConfigure: boolean;
37
+ readonly tag: string;
38
+ run(name: string, included: readonly TestItem[], excluded: readonly TestItem[]): void;
39
+ configure(): void;
40
+ }
41
+
42
+ export interface TestOutputItem {
43
+ readonly output: string;
44
+ readonly location?: Location;
45
+ }
46
+
47
+ export enum TestExecutionState {
48
+ Queued = 1,
49
+ Running = 2,
50
+ Passed = 3,
51
+ Failed = 4,
52
+ Skipped = 5,
53
+ Errored = 6
54
+ }
55
+
56
+ export interface TestMessage {
57
+ readonly expected?: string;
58
+ readonly actual?: string;
59
+ readonly location: Location;
60
+ readonly message: string | MarkdownString;
61
+ readonly contextValue?: string;
62
+ }
63
+
64
+ export namespace TestMessage {
65
+ export function is(obj: unknown): obj is TestMessage {
66
+ return isObject<TestMessage>(obj) && (MarkdownString.is(obj.message) || typeof obj.message === 'string');
67
+ }
68
+ }
69
+
70
+ export interface TestState {
71
+ readonly state: TestExecutionState;
72
+ }
73
+
74
+ export interface TestFailure extends TestState {
75
+ readonly state: TestExecutionState.Failed | TestExecutionState.Errored;
76
+ readonly messages: TestMessage[];
77
+ readonly duration?: number;
78
+ }
79
+
80
+ export namespace TestFailure {
81
+ export function is(obj: unknown): obj is TestFailure {
82
+ return isObject<TestFailure>(obj) && (obj.state === TestExecutionState.Failed || obj.state === TestExecutionState.Errored) && Array.isArray(obj.messages);
83
+ }
84
+ }
85
+
86
+ export interface TestSuccess extends TestState {
87
+ readonly state: TestExecutionState.Passed;
88
+ readonly duration?: number;
89
+ }
90
+
91
+ export interface TestStateChangedEvent {
92
+ test: TestItem;
93
+ oldState: TestState | undefined;
94
+ newState: TestState | undefined;
95
+ }
96
+
97
+ export interface TestRun {
98
+ cancel(): void;
99
+ readonly name: string;
100
+ readonly isRunning: boolean;
101
+ readonly controller: TestController;
102
+
103
+ onDidChangeProperty: Event<{ name?: string, isRunning?: boolean }>;
104
+
105
+ getTestState(item: TestItem): TestState | undefined;
106
+ onDidChangeTestState: Event<TestStateChangedEvent[]>;
107
+
108
+ getOutput(item?: TestItem): readonly TestOutputItem[];
109
+ onDidChangeTestOutput: Event<[TestItem | undefined, TestOutputItem][]>;
110
+
111
+ readonly items: readonly TestItem[];
112
+ }
113
+
114
+ export namespace TestRun {
115
+ export function is(obj: unknown): obj is TestRun {
116
+ return isObject<TestRun>(obj)
117
+ && typeof obj.cancel === 'function'
118
+ && typeof obj.name === 'string'
119
+ && typeof obj.isRunning === 'boolean'
120
+ && typeof obj.controller === 'object'
121
+ && typeof obj.onDidChangeProperty === 'function'
122
+ && typeof obj.getTestState === 'function'
123
+ && typeof obj.onDidChangeTestState === 'function'
124
+ && typeof obj.onDidChangeTestState === 'function'
125
+ && typeof obj.getOutput === 'function'
126
+ && typeof obj.onDidChangeTestOutput === 'function'
127
+ && Array.isArray(obj.items);
128
+ }
129
+ }
130
+
131
+ export interface TestItem {
132
+ readonly id: string;
133
+ readonly label: string;
134
+ readonly range?: Range;
135
+ readonly sortKey?: string;
136
+ readonly tags: string[];
137
+ readonly uri?: URI;
138
+ readonly busy: boolean;
139
+ readonly tests: readonly TestItem[];
140
+ readonly description?: string;
141
+ readonly error?: string | MarkdownString;
142
+ readonly parent: TestItem | undefined;
143
+ readonly controller: TestController | undefined;
144
+ readonly canResolveChildren: boolean;
145
+ resolveChildren(): void;
146
+ readonly path: string[];
147
+ }
148
+
149
+ export namespace TestItem {
150
+ export function is(obj: unknown): obj is TestItem {
151
+ return isObject<TestItem>(obj)
152
+ && obj.id !== undefined
153
+ && obj.label !== undefined
154
+ && Array.isArray(obj.tags)
155
+ && Array.isArray(obj.tests)
156
+ && obj.busy !== undefined
157
+ && obj.canResolveChildren !== undefined
158
+ && typeof obj.resolveChildren === 'function';
159
+ }
160
+ }
161
+
162
+ export interface TestController {
163
+ readonly id: string;
164
+ readonly label: string;
165
+ readonly tests: readonly TestItem[];
166
+ readonly testRunProfiles: readonly TestRunProfile[];
167
+ readonly testRuns: readonly TestRun[];
168
+
169
+ readonly onItemsChanged: Event<TreeDelta<string, TestItem>[]>;
170
+ readonly onRunsChanged: Event<CollectionDelta<TestRun, TestRun>>;
171
+ readonly onProfilesChanged: Event<CollectionDelta<TestRunProfile, TestRunProfile>>;
172
+
173
+ refreshTests(token: CancellationToken): Promise<void>;
174
+ clearRuns(): void;
175
+ }
176
+
177
+ export interface TestService {
178
+ clearResults(): void;
179
+ configureProfile(): void;
180
+ runTestsWithProfile(tests: TestItem[]): void;
181
+ runTests(profileKind: TestRunProfileKind, tests: TestItem[]): void;
182
+ runAllTests(profileKind: TestRunProfileKind): void;
183
+ getControllers(): TestController[];
184
+ registerTestController(controller: TestController): Disposable;
185
+ onControllersChanged: Event<CollectionDelta<string, TestController>>;
186
+
187
+ refresh(): void;
188
+ cancelRefresh(): void;
189
+ isRefreshing: boolean;
190
+ onDidChangeIsRefreshing: Event<void>;
191
+ }
192
+
193
+ export const TestContribution = Symbol('TestContribution');
194
+
195
+ export interface TestContribution {
196
+ registerTestControllers(service: TestService): void;
197
+ }
198
+
199
+ export const TestService = Symbol('TestService');
200
+
201
+ @injectable()
202
+ export class DefaultTestService implements TestService {
203
+ @inject(QuickPickService) quickpickService: QuickPickService;
204
+
205
+ private testRunCounter = 0;
206
+
207
+ private onDidChangeIsRefreshingEmitter = new Emitter<void>();
208
+ onDidChangeIsRefreshing: Event<void> = this.onDidChangeIsRefreshingEmitter.event;
209
+
210
+ private controllers: Map<string, TestController> = new Map();
211
+ private refreshing: Set<CancellationTokenSource> = new Set();
212
+ private onControllersChangedEmitter = new Emitter<CollectionDelta<string, TestController>>();
213
+
214
+ @inject(ContributionProvider) @named(TestContribution)
215
+ protected readonly contributionProvider: ContributionProvider<TestContribution>;
216
+
217
+ @postConstruct()
218
+ protected registerContributions(): void {
219
+ this.contributionProvider.getContributions().forEach(contribution => contribution.registerTestControllers(this));
220
+ }
221
+
222
+ onControllersChanged: Event<CollectionDelta<string, TestController>> = this.onControllersChangedEmitter.event;
223
+
224
+ registerTestController(controller: TestController): Disposable {
225
+ if (this.controllers.has(controller.id)) {
226
+ throw new Error('TestController already registered: ' + controller.id);
227
+ }
228
+ this.controllers.set(controller.id, controller);
229
+ this.onControllersChangedEmitter.fire({ added: [controller] });
230
+ return Disposable.create(() => {
231
+ this.controllers.delete(controller.id);
232
+ this.onControllersChangedEmitter.fire({ removed: [controller.id] });
233
+ });
234
+ }
235
+
236
+ getControllers(): TestController[] {
237
+ return Array.from(this.controllers.values());
238
+ }
239
+
240
+ refresh(): void {
241
+ const cts = new CancellationTokenSource();
242
+ this.refreshing.add(cts);
243
+
244
+ Promise.all(this.getControllers().map(controller => controller.refreshTests(cts.token))).then(() => {
245
+ this.refreshing.delete(cts);
246
+ if (this.refreshing.size === 0) {
247
+ this.onDidChangeIsRefreshingEmitter.fire();
248
+ }
249
+ });
250
+
251
+ if (this.refreshing.size === 1) {
252
+ this.onDidChangeIsRefreshingEmitter.fire();
253
+ }
254
+ }
255
+
256
+ cancelRefresh(): void {
257
+ if (this.refreshing.size > 0) {
258
+ this.refreshing.forEach(cts => cts.cancel());
259
+ this.refreshing.clear();
260
+ this.onDidChangeIsRefreshingEmitter.fire();
261
+ }
262
+ }
263
+
264
+ get isRefreshing(): boolean {
265
+ return this.refreshing.size > 0;
266
+ }
267
+
268
+ runAllTests(profileKind: TestRunProfileKind): void {
269
+ this.getControllers().forEach(controller => {
270
+ this.runTestForController(controller, profileKind, controller.tests);
271
+ });
272
+ }
273
+
274
+ protected async runTestForController(controller: TestController, profileKind: TestRunProfileKind, items: readonly TestItem[]): Promise<void> {
275
+ const runProfiles = controller.testRunProfiles.filter(profile => profile.kind === profileKind);
276
+ let activeProfile;
277
+ if (runProfiles.length === 1) {
278
+ activeProfile = runProfiles[0];
279
+ } else if (runProfiles.length > 1) {
280
+ const defaultProfile = runProfiles.find(p => p.isDefault);
281
+ if (defaultProfile) {
282
+ activeProfile = defaultProfile;
283
+ } else {
284
+
285
+ activeProfile = await this.pickProfile(runProfiles, nls.localizeByDefault('Pick a test profile to use'));
286
+ }
287
+ }
288
+ if (activeProfile) {
289
+ activeProfile.run(`Test run #${this.testRunCounter++}`, items, []);
290
+ }
291
+ }
292
+
293
+ protected async pickProfile(runProfiles: readonly TestRunProfile[], title: string): Promise<TestRunProfile | undefined> {
294
+ if (runProfiles.length === 0) {
295
+ return undefined;
296
+ }
297
+ // eslint-disable-next-line arrow-body-style
298
+ const picks = runProfiles.map(profile => {
299
+ let iconClasses;
300
+ if (profile.kind === TestRunProfileKind.Run) {
301
+ iconClasses = codiconArray('run');
302
+ } else if (profile.kind === TestRunProfileKind.Debug) {
303
+ iconClasses = codiconArray('debug-alt');
304
+ }
305
+ return {
306
+ iconClasses,
307
+ label: profile.label,
308
+ profile: profile
309
+ };
310
+ });
311
+
312
+ return (await this.quickpickService.show(picks, { title: title }))?.profile;
313
+
314
+ }
315
+
316
+ runTests(profileKind: TestRunProfileKind, items: TestItem[]): void {
317
+ groupBy(items, item => item.controller).forEach((tests, controller) => {
318
+ if (controller) {
319
+ this.runTestForController(controller, profileKind, tests);
320
+ }
321
+ });
322
+ }
323
+
324
+ runTestsWithProfile(items: TestItem[]): void {
325
+ groupBy(items, item => item.controller).forEach((tests, controller) => {
326
+ if (controller) {
327
+ this.pickProfile(controller.testRunProfiles, nls.localizeByDefault('Pick a test profile to use')).then(activeProfile => {
328
+ if (activeProfile) {
329
+ activeProfile.run(`Test run #${this.testRunCounter++}`, items, []);
330
+ }
331
+ });
332
+ }
333
+ });
334
+ }
335
+
336
+ configureProfile(): void {
337
+ const profiles: TestRunProfile[] = [];
338
+
339
+ for (const controller of this.controllers.values()) {
340
+ profiles.push(...controller.testRunProfiles);
341
+ }
342
+ ;
343
+ this.pickProfile(profiles.filter(profile => profile.canConfigure), nls.localizeByDefault('Select a profile to update')).then(profile => {
344
+ if (profile) {
345
+ profile.configure();
346
+ }
347
+ });
348
+ }
349
+
350
+ clearResults(): void {
351
+ for (const controller of this.controllers.values()) {
352
+ controller.clearRuns();
353
+ }
354
+ }
355
+ }