Skip to content

Commit

Permalink
[vscode] Support TestRunRequest.preserveFocus
Browse files Browse the repository at this point in the history
Add a progress service for tests to support activation of views
Forward Test Run to be started event to progress service to activate view if needed

fixes #13759

contributed on behalf of STMicroelectronics

Signed-off-by: Remi Schnekenburger <rschnekenburger@eclipsesource.com>
  • Loading branch information
rschnekenbu committed Jun 24, 2024
1 parent 49b8735 commit f581b5a
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2251,6 +2251,7 @@ export interface TestingMain {

// Test runs

$notifyOnTestRunRequest(preserveFocus: boolean): void;
$notifyTestRunCreated(controllerId: string, run: TestRunDTO): void;
$notifyTestStateChanged(controllerId: string, runId: string, stateChanges: TestStateChangeDTO[], outputChanges: TestOutputDTO[]): void;
$notifyTestRunEnded(controllerId: string, runId: string): void;
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/src/common/test-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export interface TestRunRequestDTO {
name: string;
includedTests: string[][]; // array of paths
excludedTests: string[][]; // array of paths
preserveFocus: boolean;
}

export interface TestItemReference {
Expand Down
9 changes: 7 additions & 2 deletions packages/plugin-ext/src/main/browser/test-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,14 @@ class TestRunProfileImpl implements TestRunProfile {
this.proxy.$onConfigureRunProfile(this.controllerId, this.id);
}

run(name: string, included: TestItem[], excluded: TestItem[]): void {
run(name: string, included: TestItem[], excluded: TestItem[], preserveFocus: boolean): void {
this.proxy.$onRunControllerTests([{
controllerId: this.controllerId,
name,
profileId: this.id,
includedTests: included.map(item => itemToPath(item)),
excludedTests: excluded.map(item => itemToPath(item))
excludedTests: excluded.map(item => itemToPath(item)),
preserveFocus
}]);
}
}
Expand Down Expand Up @@ -624,4 +625,8 @@ export class TestingMainImpl implements TestingMain {
$notifyTestRunEnded(controllerId: string, runId: string): void {
this.withController(controllerId).withRun(runId).ended();
}

$notifyOnTestRunRequest(preserveFocus: boolean): void {
this.testService.testAboutToRun(preserveFocus);
}
}
7 changes: 4 additions & 3 deletions packages/plugin-ext/src/plugin/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export class TestControllerImpl implements theia.TestController {
}

createTestRun(request: theia.TestRunRequest, name?: string, persist: boolean = true): theia.TestRun {
this.proxy.$notifyOnTestRunRequest(request.preserveFocus);
return this.testRunStarted(request, name || '', persist, true);
}

Expand Down Expand Up @@ -162,7 +163,7 @@ export class TestControllerImpl implements theia.TestController {
return run;
}

runTestsForUI(profileId: string, name: string, includedTests: string[][], excludedTests: string[][]): void {
runTestsForUI(profileId: string, name: string, includedTests: string[][], excludedTests: string[][], preserveFocus: boolean): void {
const profile = this.getProfile(profileId);
if (!profile) {
console.error(`No test run profile found for controller ${this.id} with id ${profileId} `);
Expand Down Expand Up @@ -197,7 +198,7 @@ export class TestControllerImpl implements theia.TestController {
.filter(isDefined);

const request = new TestRunRequest(
includeTests, excludeTests, profile, false // don't support continuous run yet
includeTests, excludeTests, profile, false /* don't support continuous run yet */, preserveFocus
);

const run = this.testRunStarted(request, name, false, false);
Expand Down Expand Up @@ -444,7 +445,7 @@ export class TestingExtImpl implements TestingExt {
}

runTestsForUI(req: TestRunRequestDTO): void {
this.withController(req.controllerId).runTestsForUI(req.profileId, req.name, req.includedTests, req.excludedTests);
this.withController(req.controllerId).runTestsForUI(req.profileId, req.name, req.includedTests, req.excludedTests, req.preserveFocus);
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3329,6 +3329,7 @@ export class TestRunRequest implements theia.TestRunRequest {
public readonly exclude: theia.TestItem[] | undefined = undefined,
public readonly profile: theia.TestRunProfile | undefined = undefined,
public readonly continuous: boolean | undefined = undefined,
public readonly preserveFocus: boolean = true
) { }
}

Expand Down
11 changes: 10 additions & 1 deletion packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16242,13 +16242,22 @@ export module '@theia/plugin' {
*/
readonly continuous?: boolean;

/**
* Controls how test Test Results view is focused. If true, the editor
* will keep the maintain the user's focus. If false, the editor will
* prefer to move focus into the Test Results view, although
* this may be configured by users.
*/
readonly preserveFocus: boolean;

/**
* @param include Array of specific tests to run, or undefined to run all tests
* @param exclude An array of tests to exclude from the run.
* @param profile The run profile used for this request.
* @param continuous Whether to run tests continuously as source changes.
* @param preserveFocus Whether to preserve the user's focus when the run is started
*/
constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile, continuous?: boolean);
constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile, continuous?: boolean, preserveFocus?: boolean);
}

/**
Expand Down
16 changes: 13 additions & 3 deletions packages/test/src/browser/test-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export interface TestRunProfile {
isDefault: boolean;
readonly canConfigure: boolean;
readonly tag: string;
run(name: string, included: readonly TestItem[], excluded: readonly TestItem[]): void;
run(name: string, included: readonly TestItem[], excluded: readonly TestItem[], preserveFocus: boolean): void;
configure(): void;
}

Expand Down Expand Up @@ -185,6 +185,9 @@ export interface TestService {
registerTestController(controller: TestController): Disposable;
onControllersChanged: Event<CollectionDelta<string, TestController>>;

testAboutToRun(preserveFocus: boolean): void;
onWillStartTestRun: Event<boolean>;

refresh(): void;
cancelRefresh(): void;
isRefreshing: boolean;
Expand Down Expand Up @@ -212,6 +215,9 @@ export class DefaultTestService implements TestService {
private refreshing: Set<CancellationTokenSource> = new Set();
private onControllersChangedEmitter = new Emitter<CollectionDelta<string, TestController>>();

private onWillStartTestRunEmitter = new Emitter<boolean>();
onWillStartTestRun: Event<boolean> = this.onWillStartTestRunEmitter.event;

@inject(ContributionProvider) @named(TestContribution)
protected readonly contributionProvider: ContributionProvider<TestContribution>;

Expand Down Expand Up @@ -287,7 +293,7 @@ export class DefaultTestService implements TestService {
}
}
if (activeProfile) {
activeProfile.run(`Test run #${this.testRunCounter++}`, items, []);
activeProfile.run(`Test run #${this.testRunCounter++}`, items, [], true);
}
}

Expand Down Expand Up @@ -343,13 +349,17 @@ export class DefaultTestService implements TestService {
if (controller) {
this.pickProfile(controller.testRunProfiles, nls.localizeByDefault('Pick a test profile to use')).then(activeProfile => {
if (activeProfile) {
activeProfile.run(`Test run #${this.testRunCounter++}`, items, []);
activeProfile.run(`Test run #${this.testRunCounter++}`, items, [], true);
}
});
}
});
}

testAboutToRun(preserveFocus: boolean): void {
this.onWillStartTestRunEmitter.fire(preserveFocus);
}

selectDefaultProfile(): void {
this.pickProfileKind().then(kind => {
const profiles = this.getControllers().flatMap(c => c.testRunProfiles).filter(profile => profile.kind === kind);
Expand Down
54 changes: 54 additions & 0 deletions packages/test/src/browser/view/test-execution-progress-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// *****************************************************************************
// Copyright (C) 2024 STMicroelectronics and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { inject, injectable } from '@theia/core/shared/inversify';
import { ApplicationShell, Widget, WidgetManager } from '@theia/core/lib/browser';
import { TestResultViewContribution } from './test-result-view-contribution';
import { TestService } from '../test-service';

export interface TestExecutionProgressService {
start(): void;
}

export const TestExecutionProgressService = Symbol('TestExecutionProgressService');

@injectable()
export class DefaultTestExecutionProgressService implements TestExecutionProgressService {

@inject(WidgetManager)
protected readonly widgetManager: WidgetManager;

@inject(ApplicationShell)
protected readonly shell: ApplicationShell;

@inject(TestResultViewContribution)
protected readonly testResultView: TestResultViewContribution;

@inject(TestService)
protected readonly testService: TestService;

start(): void {
this.testService.onWillStartTestRun(preserveFocus => {
if (!preserveFocus) {
this.openTestResultView();
}
});
}

async openTestResultView(): Promise<Widget> {
return this.testResultView.openView({ activate: true });
}
}
11 changes: 10 additions & 1 deletion packages/test/src/browser/view/test-view-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { TestRunTree, TestRunTreeWidget } from './test-run-widget';
import { TestResultViewContribution } from './test-result-view-contribution';
import { TEST_RUNS_CONTEXT_MENU, TestRunViewContribution } from './test-run-view-contribution';
import { TestContextKeyService } from './test-context-key-service';
import { DefaultTestExecutionProgressService, TestExecutionProgressService } from './test-execution-progress-service';

export default new ContainerModule(bind => {

Expand Down Expand Up @@ -105,7 +106,15 @@ export default new ContainerModule(bind => {
bind(TabBarToolbarContribution).toService(TestRunViewContribution);
bind(TestExecutionStateManager).toSelf().inSingletonScope();
bind(TestOutputUIModel).toSelf().inSingletonScope();

bind(TestExecutionProgressService).to(DefaultTestExecutionProgressService).inSingletonScope();
bind(FrontendApplicationContribution).toDynamicValue(({container}) => {
const testExecutionProgressService: TestExecutionProgressService = container.get(TestExecutionProgressService);
return {
onStart: () => {
testExecutionProgressService.start();
},
};
}).inSingletonScope();
});

export function createTestTreeContainer(parent: interfaces.Container): Container {
Expand Down

0 comments on commit f581b5a

Please sign in to comment.