// ***************************************************************************** // Copyright (C) 2023 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 { AbstractViewContribution, FrontendApplicationContribution, ViewContainerTitleOptions, Widget, codicon } from '@theia/core/lib/browser'; import { Command, CommandRegistry, MenuModelRegistry, nls } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { TestItem, TestRunProfileKind, TestService } from '../test-service'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { TestTreeWidget } from './test-tree-widget'; import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { TestCommandId } from '../constants'; import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/navigation-location-service'; import { NavigationLocation } from '@theia/editor/lib/browser/navigation/navigation-location'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileNavigatorCommands } from '@theia/navigator/lib/browser/file-navigator-commands'; export const PLUGIN_TEST_VIEW_TITLE_MENU = ['plugin_test', 'title']; export namespace TestViewCommands { /** * Command which refreshes all test. */ export const REFRESH: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.RefreshTestsAction, label: 'Refresh Tests', category: 'Test', iconClass: codicon('refresh') }); /** * Command which cancels the refresh */ export const CANCEL_REFRESH: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.CancelTestRefreshAction, label: 'Cancel Test Refresh', category: 'Test', iconClass: codicon('stop') }); export const RUN_ALL_TESTS: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.RunAllAction, label: 'Run All Tests', category: 'Test', iconClass: codicon('run-all') }); export const DEBUG_ALL_TESTS: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.DebugAllAction, label: 'Debug Tests', category: 'Test', iconClass: codicon('debug-all') }); export const RUN_TEST: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.RunAction, label: 'Run Test', category: 'Test', iconClass: codicon('run') }); export const RUN_TEST_WITH_PROFILE: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.RunUsingProfileAction, category: 'Test', label: 'Execute Using Profile...' }); export const DEBUG_TEST: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.DebugAction, label: 'Debug Test', category: 'Test', iconClass: codicon('debug-alt') }); export const CANCEL_ALL_RUNS: Command = Command.toLocalizedCommand({ id: 'testing.cancelAllRuns', label: 'Cancel All Test Runs', category: 'Test', iconClass: codicon('debug-stop') }, 'theia/test/cancelAllTestRuns', nls.getDefaultKey('Test')); export const CANCEL_RUN: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.CancelTestRunAction, label: 'Cancel Test Run', category: 'Test', iconClass: codicon('debug-stop') }); export const GOTO_TEST: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.GoToTest, label: 'Go to Test', category: 'Test', iconClass: codicon('go-to-file') }); export const CONFIGURE_PROFILES: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.ConfigureTestProfilesAction, label: 'Configure Test Profiles', category: 'Test' }); export const SELECT_DEFAULT_PROFILES: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.SelectDefaultTestProfiles, label: 'Select Default Profile', category: 'Test' }); export const CLEAR_ALL_RESULTS: Command = Command.toDefaultLocalizedCommand({ id: TestCommandId.ClearTestResultsAction, label: 'Clear All Results', category: 'Test', iconClass: codicon('trash') }); } export const TEST_VIEW_CONTEXT_MENU = ['test-view-context-menu']; export const TEST_VIEW_INLINE_MENU = [...TEST_VIEW_CONTEXT_MENU, 'inline']; export const TEST_VIEW_CONTAINER_ID = 'test-view-container'; export const TEST_VIEW_CONTAINER_TITLE_OPTIONS: ViewContainerTitleOptions = { label: nls.localizeByDefault('Testing'), iconClass: codicon('beaker'), closeable: true }; @injectable() export class TestViewContribution extends AbstractViewContribution implements FrontendApplicationContribution, TabBarToolbarContribution { @inject(TestService) protected readonly testService: TestService; @inject(ContextKeyService) protected readonly contextKeys: ContextKeyService; @inject(NavigationLocationService) navigationService: NavigationLocationService; @inject(FileService) fileSystem: FileService; constructor() { super({ viewContainerId: TEST_VIEW_CONTAINER_ID, widgetId: TestTreeWidget.ID, widgetName: nls.localizeByDefault('Test Explorer'), defaultWidgetOptions: { area: 'left', rank: 600, } }); } async initializeLayout(): Promise { await this.openView({ activate: false }); } override registerCommands(commands: CommandRegistry): void { super.registerCommands(commands); commands.registerCommand(TestViewCommands.REFRESH, { isEnabled: w => this.withWidget(w, () => !this.testService.isRefreshing), isVisible: w => this.withWidget(w, () => !this.testService.isRefreshing), execute: () => this.testService.refresh() }); commands.registerCommand(TestViewCommands.CANCEL_REFRESH, { isEnabled: w => this.withWidget(w, () => this.testService.isRefreshing), isVisible: w => this.withWidget(w, () => this.testService.isRefreshing), execute: () => this.testService.cancelRefresh() }); commands.registerCommand(TestViewCommands.RUN_ALL_TESTS, { isEnabled: w => this.withWidget(w, () => true), isVisible: w => this.withWidget(w, () => true), execute: () => this.testService.runAllTests(TestRunProfileKind.Run) }); commands.registerCommand(TestViewCommands.DEBUG_ALL_TESTS, { isEnabled: w => this.withWidget(w, () => true), isVisible: w => this.withWidget(w, () => true), execute: () => this.testService.runAllTests(TestRunProfileKind.Debug) }); commands.registerCommand(TestViewCommands.RUN_TEST, { isEnabled: t => TestItem.is(t), isVisible: t => TestItem.is(t), execute: t => { this.testService.runTests(TestRunProfileKind.Run, [t]); } }); commands.registerCommand(TestViewCommands.SELECT_DEFAULT_PROFILES, { isEnabled: t => TestItem.is(t), isVisible: t => TestItem.is(t), execute: () => { this.testService.selectDefaultProfile(); } }); commands.registerCommand(TestViewCommands.DEBUG_TEST, { isEnabled: t => TestItem.is(t), isVisible: t => TestItem.is(t), execute: t => { this.testService.runTests(TestRunProfileKind.Debug, [t]); } }); commands.registerCommand(TestViewCommands.RUN_TEST_WITH_PROFILE, { isEnabled: t => TestItem.is(t), isVisible: t => TestItem.is(t), execute: t => { this.testService.runTestsWithProfile([t]); } }); commands.registerCommand(TestViewCommands.CANCEL_ALL_RUNS, { isEnabled: w => this.withWidget(w, () => true), isVisible: w => this.withWidget(w, () => true), execute: () => this.cancelAllRuns() }); commands.registerCommand(TestViewCommands.GOTO_TEST, { isEnabled: t => TestItem.is(t) && !!t.uri, isVisible: t => TestItem.is(t) && !!t.uri, execute: t => { if (TestItem.is(t)) { this.fileSystem.resolve(t.uri!).then(stat => { if (stat.isFile) { this.navigationService.reveal(NavigationLocation.create(t.uri!, t.range ? t.range.start : { line: 0, character: 0 })); } else { commands.executeCommand(FileNavigatorCommands.REVEAL_IN_NAVIGATOR.id, t.uri!); } }); } } }); commands.registerCommand(TestViewCommands.CONFIGURE_PROFILES, { execute: () => { this.testService.configureProfile(); } }); } protected cancelAllRuns(): void { this.testService.getControllers().forEach(controller => controller.testRuns.forEach(run => run.cancel())); } override registerMenus(menus: MenuModelRegistry): void { super.registerMenus(menus); menus.registerMenuAction(TEST_VIEW_INLINE_MENU, { commandId: TestViewCommands.RUN_TEST.id, order: 'a' }); menus.registerMenuAction(TEST_VIEW_INLINE_MENU, { commandId: TestViewCommands.DEBUG_TEST.id, order: 'aa' }); menus.registerMenuAction(TEST_VIEW_INLINE_MENU, { commandId: TestViewCommands.GOTO_TEST.id, order: 'aaa' }); menus.registerMenuAction(TEST_VIEW_CONTEXT_MENU, { commandId: TestViewCommands.RUN_TEST_WITH_PROFILE.id, order: 'aaaa' }); menus.registerMenuAction(TEST_VIEW_CONTEXT_MENU, { commandId: TestViewCommands.SELECT_DEFAULT_PROFILES.id, order: 'aaaaa' }); menus.registerSubmenu([...PLUGIN_TEST_VIEW_TITLE_MENU, TestViewCommands.RUN_ALL_TESTS.id], '', { contextKeyOverlay: { 'testing.profile.context.group': 'run' } }); menus.registerSubmenu([...PLUGIN_TEST_VIEW_TITLE_MENU, TestViewCommands.DEBUG_ALL_TESTS.id], '', { contextKeyOverlay: { 'testing.profile.context.group': 'debug' } }); } registerToolbarItems(toolbar: TabBarToolbarRegistry): void { toolbar.registerItem({ id: TestViewCommands.REFRESH.id, command: TestViewCommands.REFRESH.id, priority: 0, onDidChange: this.testService.onDidChangeIsRefreshing }); toolbar.registerItem({ id: TestViewCommands.CANCEL_REFRESH.id, command: TestViewCommands.CANCEL_REFRESH.id, priority: 0, onDidChange: this.testService.onDidChangeIsRefreshing }); toolbar.registerItem({ id: TestViewCommands.RUN_ALL_TESTS.id, command: TestViewCommands.RUN_ALL_TESTS.id, menuPath: PLUGIN_TEST_VIEW_TITLE_MENU, priority: 1, isVisible(widget): boolean { return widget instanceof TestTreeWidget && widget.id === TestTreeWidget.ID; } }); toolbar.registerItem({ id: TestViewCommands.DEBUG_ALL_TESTS.id, command: TestViewCommands.DEBUG_ALL_TESTS.id, menuPath: PLUGIN_TEST_VIEW_TITLE_MENU, priority: 2, isVisible(widget): boolean { return widget instanceof TestTreeWidget && widget.id === TestTreeWidget.ID; } }); toolbar.registerItem({ id: TestViewCommands.CANCEL_ALL_RUNS.id, command: TestViewCommands.CANCEL_ALL_RUNS.id, priority: 3 }); } protected withWidget(widget: Widget | undefined = this.tryGetWidget(), cb: (widget: TestTreeWidget) => T): T | false { if (widget instanceof TestTreeWidget && widget.id === TestTreeWidget.ID) { return cb(widget); } return false; } }