diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsPresenter.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsPresenter.java index 91913bafd1..986afaf813 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsPresenter.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsPresenter.java @@ -64,7 +64,6 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { private final EditCommandsView view; private final WorkspaceServiceClient workspaceServiceClient; private final CommandManager commandManager; - private final String workspaceId; private final DtoFactory dtoFactory; private final CommandTypeRegistry commandTypeRegistry; private final DialogFactory dialogFactory; @@ -72,14 +71,17 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { private final CoreLocalizationConstant coreLocale; private final Provider selectCommandActionProvider; private final Set configurationChangedListeners; + private final AppContext appContext; /** Set of the existing command names. */ private final Set commandNames; private CommandConfigurationPage editedPage; /** Command that being edited. */ private CommandConfiguration editedCommand; /** Name of the edited command before editing. */ - private String editedCommandOriginName; - private String editedCommandOriginPreviewUrl; + String editedCommandOriginName; + String editedCommandOriginPreviewUrl; + String workspaceId; + CommandProcessingCallback commandProcessingCallback; @Inject protected EditCommandsPresenter(EditCommandsView view, @@ -95,7 +97,6 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { this.view = view; this.workspaceServiceClient = workspaceServiceClient; this.commandManager = commandManager; - this.workspaceId = appContext.getWorkspace().getId(); this.dtoFactory = dtoFactory; this.commandTypeRegistry = commandTypeRegistry; this.dialogFactory = dialogFactory; @@ -103,7 +104,7 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { this.coreLocale = coreLocale; this.selectCommandActionProvider = selectCommandActionProvider; this.view.setDelegate(this); - + this.appContext = appContext; configurationChangedListeners = new HashSet<>(); commandNames = new HashSet<>(); } @@ -134,6 +135,7 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { updateCommand(selectedConfiguration).then(new Operation() { @Override public void apply(UsersWorkspaceDto arg) throws OperationException { + commandProcessingCallback = getCommandProcessingCallback(); fetchCommands(); fireConfigurationUpdated(selectedConfiguration); } @@ -172,6 +174,7 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { @Override public void onCancelClicked() { + commandProcessingCallback = getCommandProcessingCallback(); fetchCommands(); } @@ -293,6 +296,7 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { @Override public void apply(UsersWorkspaceDto arg) throws OperationException { view.selectNextItem(); + commandProcessingCallback = getCommandProcessingCallback(); fetchCommands(); fireConfigurationRemoved(selectedConfiguration); } @@ -324,6 +328,20 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { view.close(); } + @Override + public void onEnterClicked() { + if (view.isCancelButtonInFocus()) { + onCancelClicked(); + return; + } + + if (view.isCloseButtonInFocus()) { + onCloseClicked(); + return; + } + onSaveClicked(); + } + private void reset() { editedCommand = null; editedCommandOriginName = null; @@ -436,6 +454,7 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { /** Show dialog. */ public void show() { + workspaceId = appContext.getWorkspaceId(); fetchCommands(); view.show(); } @@ -497,6 +516,11 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { } view.setData(categories); view.setFilterState(!commandConfigurations.isEmpty()); + + if (commandProcessingCallback != null) { + commandProcessingCallback.onCompleted(); + commandProcessingCallback = null; + } } }).catchError(new Operation() { @Override @@ -533,6 +557,15 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { } } + private CommandProcessingCallback getCommandProcessingCallback() { + return new CommandProcessingCallback() { + @Override + public void onCompleted() { + view.setCloseButtonInFocus(); + } + }; + } + public void addConfigurationsChangedListener(ConfigurationChangedListener listener) { configurationChangedListeners.add(listener); } @@ -549,4 +582,9 @@ public class EditCommandsPresenter implements EditCommandsView.ActionDelegate { void onConfigurationsUpdated(CommandConfiguration command); } + + interface CommandProcessingCallback { + /** Called when handling of command is completed successfully. */ + void onCompleted(); + } } diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsView.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsView.java index 4a0c6af0e6..9acad354fb 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsView.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsView.java @@ -17,6 +17,7 @@ import org.eclipse.che.ide.extension.machine.client.command.CommandConfiguration import org.eclipse.che.ide.extension.machine.client.command.CommandType; import org.eclipse.che.commons.annotation.Nullable; + import java.util.List; import java.util.Map; @@ -67,7 +68,7 @@ public interface EditCommandsView extends View /** Sets enabled state of the 'Apply' button. */ void setSaveButtonState(boolean enabled); - + /** Sets enabled state of the filter input field. */ void setFilterState(boolean enabled); @@ -82,6 +83,15 @@ public interface EditCommandsView extends View @Nullable CommandConfiguration getSelectedConfiguration(); + /** Sets the focus on the 'Close' button. */ + void setCloseButtonInFocus(); + + /** Returns {@code true} if cancel button is in the focus and {@code false} - otherwise. */ + boolean isCancelButtonInFocus(); + + /** Returns {@code true} if close button is in the focus and {@code false} - otherwise. */ + boolean isCloseButtonInFocus(); + /** Action handler for the view actions/controls. */ interface ActionDelegate { @@ -106,6 +116,9 @@ public interface EditCommandsView extends View /** Called when 'Execute' button is clicked. */ void onExecuteClicked(); + /** Performs any actions appropriate in response to the user having clicked the Enter key. */ + void onEnterClicked(); + /** * Called when some command configuration is selected. * diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsViewImpl.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsViewImpl.java index c3d9a763f6..ecb33b581c 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsViewImpl.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsViewImpl.java @@ -49,6 +49,7 @@ import org.eclipse.che.ide.api.icon.IconRegistry; import org.eclipse.che.ide.extension.machine.client.MachineLocalizationConstant; import org.eclipse.che.ide.extension.machine.client.command.CommandConfiguration; import org.eclipse.che.ide.extension.machine.client.command.CommandType; +import org.eclipse.che.ide.ui.WidgetFocusTracker; import org.eclipse.che.ide.ui.list.CategoriesList; import org.eclipse.che.ide.ui.list.Category; import org.eclipse.che.ide.ui.list.CategoryRenderer; @@ -73,10 +74,12 @@ public class EditCommandsViewImpl extends Window implements EditCommandsView { private final EditCommandResources commandResources; private final IconRegistry iconRegistry; + private final WidgetFocusTracker widgetFocusTracker; private final CoreLocalizationConstant coreLocale; - private final Button cancelButton; - private final Button saveButton; private final Label hintLabel; + private Button cancelButton; + private Button saveButton; + private Button closeButton; private final CategoryRenderer projectImporterRenderer = new CategoryRenderer() { @@ -136,11 +139,13 @@ public class EditCommandsViewImpl extends Window implements EditCommandsView { EditCommandResources commandResources, MachineLocalizationConstant machineLocale, CoreLocalizationConstant coreLocale, - IconRegistry iconRegistry) { + IconRegistry iconRegistry, + WidgetFocusTracker widgetFocusTracker) { this.commandResources = commandResources; this.machineLocale = machineLocale; this.coreLocale = coreLocale; this.iconRegistry = iconRegistry; + this.widgetFocusTracker = widgetFocusTracker; selectConfiguration = null; categories = new HashMap<>(); @@ -178,24 +183,7 @@ public class EditCommandsViewImpl extends Window implements EditCommandsView { previewUrlPanel.setVisible(false); contentPanel.clear(); - saveButton = createButton(coreLocale.save(), "window-edit-configurations-save", new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - delegate.onSaveClicked(); - } - }); - saveButton.addStyleName(this.resources.windowCss().primaryButton()); - overFooter.add(saveButton); - - cancelButton = createButton(coreLocale.cancel(), "window-edit-configurations-cancel", new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - delegate.onCancelClicked(); - } - }); - overFooter.add(cancelButton); - - createFooterButton(); + createButtons(); resetFilter(); getWidget().getElement().getStyle().setPadding(0, Style.Unit.PX); @@ -357,14 +345,31 @@ public class EditCommandsViewImpl extends Window implements EditCommandsView { renderCategoriesList(categories); } - private void createFooterButton() { - final Button closeButton = createButton(coreLocale.close(), "window-edit-configurations-close", - new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - delegate.onCloseClicked(); - } - }); + private void createButtons() { + saveButton = createButton(coreLocale.save(), "window-edit-configurations-save", new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + delegate.onSaveClicked(); + } + }); + saveButton.addStyleName(this.resources.windowCss().primaryButton()); + overFooter.add(saveButton); + + cancelButton = createButton(coreLocale.cancel(), "window-edit-configurations-cancel", new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + delegate.onCancelClicked(); + } + }); + overFooter.add(cancelButton); + + closeButton = createButton(coreLocale.close(), "window-edit-configurations-close", + new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + delegate.onCloseClicked(); + } + }); closeButton.addDomHandler(new BlurHandler() { @Override public void onBlur(BlurEvent blurEvent) { @@ -398,11 +403,13 @@ public class EditCommandsViewImpl extends Window implements EditCommandsView { super.show(focusPanel); configurationName.setText(""); configurationPreviewUrl.setText(""); + trackFocusForWidgets(); } @Override public void close() { this.hide(); + unTrackFocusForWidgets(); } @Override @@ -472,6 +479,11 @@ public class EditCommandsViewImpl extends Window implements EditCommandsView { return selectConfiguration; } + @Override + public void setCloseButtonInFocus() { + closeButton.setFocus(true); + } + @UiHandler("configurationName") public void onNameKeyUp(KeyUpEvent event) { delegate.onNameChanged(); @@ -492,16 +504,35 @@ public class EditCommandsViewImpl extends Window implements EditCommandsView { @Override protected void onEnterClicked() { - if (saveButton.isEnabled()) { - delegate.onSaveClicked(); - } + delegate.onEnterClicked(); } @Override protected void onClose() { setSelectedConfiguration(selectConfiguration); + unTrackFocusForWidgets(); + } + + @Override + public boolean isCancelButtonInFocus() { + return widgetFocusTracker.isWidgetFocused(cancelButton); + } + + @Override + public boolean isCloseButtonInFocus() { + return widgetFocusTracker.isWidgetFocused(closeButton); + } + + private void trackFocusForWidgets() { + widgetFocusTracker.subscribe(cancelButton); + widgetFocusTracker.subscribe(closeButton); + } + + private void unTrackFocusForWidgets() { + widgetFocusTracker.unSubscribe(cancelButton); + widgetFocusTracker.unSubscribe(closeButton); } interface EditCommandsViewImplUiBinder extends UiBinder { } -} \ No newline at end of file +} diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsPresenterTest.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsPresenterTest.java new file mode 100644 index 0000000000..9fd8a3e47a --- /dev/null +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/test/java/org/eclipse/che/ide/extension/machine/client/command/edit/EditCommandsPresenterTest.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.extension.machine.client.command.edit; + +import org.eclipse.che.api.machine.shared.dto.CommandDto; +import org.eclipse.che.api.promises.client.Function; +import org.eclipse.che.api.promises.client.Operation; +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.api.workspace.gwt.client.WorkspaceServiceClient; +import org.eclipse.che.api.workspace.shared.dto.UsersWorkspaceDto; +import org.eclipse.che.ide.CoreLocalizationConstant; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.dto.DtoFactory; +import org.eclipse.che.ide.extension.machine.client.MachineLocalizationConstant; +import org.eclipse.che.ide.extension.machine.client.command.CommandConfiguration; +import org.eclipse.che.ide.extension.machine.client.command.CommandManager; +import org.eclipse.che.ide.extension.machine.client.command.CommandType; +import org.eclipse.che.ide.extension.machine.client.command.CommandTypeRegistry; +import org.eclipse.che.ide.ui.dialogs.DialogFactory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** @author Roman Nikitenko */ +@RunWith(MockitoJUnitRunner.class) +public class EditCommandsPresenterTest { + + private static String WORKSPACE_ID = "workspaceId"; + private static String COMMAND_NAME = "commandName"; + + @Mock + private EditCommandsView view; + @Mock + private WorkspaceServiceClient workspaceServiceClient; + @Mock + private CommandManager commandManager; + @Mock + private DtoFactory dtoFactory; + @Mock + private CommandTypeRegistry commandTypeRegistry; + @Mock + private AppContext appContext; + @Mock + private DialogFactory dialogFactory; + @Mock + private MachineLocalizationConstant machineLocale; + @Mock + private CoreLocalizationConstant coreLocale; + @Mock + private UsersWorkspaceDto workspace; + + @Mock + private Promise> commandsPromise; + @Mock + private Promise workspacePromise; + @Mock + private Promise> commandConfigurationPromise; + @Captor + private ArgumentCaptor, List>> commandsCaptor; + @Captor + private ArgumentCaptor>> commandConfigurationCaptor; + @Captor + private ArgumentCaptor> workspaceCaptor; + + + @InjectMocks + private EditCommandsPresenter presenter; + + @Before + public void setUp() { + presenter.editedCommandOriginName = COMMAND_NAME; + presenter.workspaceId = WORKSPACE_ID; + when(appContext.getWorkspace()).thenReturn(workspace); + when(workspace.getId()).thenReturn(WORKSPACE_ID); + when(workspaceServiceClient.getCommands(anyString())).thenReturn(commandsPromise); + when(commandsPromise.then((Function, List>)anyObject())) + .thenReturn(commandConfigurationPromise); + when(commandConfigurationPromise.then((Operation>)anyObject())).thenReturn(commandConfigurationPromise); + when(workspaceServiceClient.updateCommand(anyString(), anyObject())).thenReturn(workspacePromise); + } + + @Test + public void onEnterClickedWhenCancelButtonInFocus() throws Exception { + when(view.isCancelButtonInFocus()).thenReturn(true); + CommandDto command = mock(CommandDto.class); + CommandConfiguration commandConfiguration = mock(CommandConfiguration.class); + List commands = new ArrayList<>(1); + List confiqurations = new ArrayList<>(1); + commands.add(command); + confiqurations.add(commandConfiguration); + + presenter.onEnterClicked(); + + verify(view).setCancelButtonState(false); + verify(view).setSaveButtonState(false); + verify(workspaceServiceClient).getCommands(anyString()); + + verify(commandsPromise).then(commandsCaptor.capture()); + commandsCaptor.getValue().apply(commands); + + verify(commandConfigurationPromise).then(commandConfigurationCaptor.capture()); + commandConfigurationCaptor.getValue().apply(confiqurations); + + verify(view).setData(anyObject()); + verify(view).setFilterState(anyBoolean()); + verify(view).setCloseButtonInFocus(); + + verify(view, never()).close(); + verify(workspaceServiceClient, never()).updateCommand(anyString(), anyObject()); + verify(workspaceServiceClient, never()).deleteCommand(anyString(), anyString()); + } + + @Test + public void onEnterClickedWhenCloseButtonInFocus() throws Exception { + when(view.isCloseButtonInFocus()).thenReturn(true); + + presenter.onEnterClicked(); + + verify(view).close(); + verify(workspaceServiceClient, never()).getCommands(anyString()); + verify(workspaceServiceClient, never()).updateCommand(anyString(), anyObject()); + verify(workspaceServiceClient, never()).deleteCommand(anyString(), anyString()); + } + + @Test + public void onEnterClickedWhenSaveButtonInFocus() throws Exception { + when(view.isCancelButtonInFocus()).thenReturn(false); + when(view.isCloseButtonInFocus()).thenReturn(false); + CommandDto command = mock(CommandDto.class); + CommandConfiguration commandConfiguration = mock(CommandConfiguration.class); + List commands = new ArrayList<>(1); + List confiqurations = new ArrayList<>(1); + commands.add(command); + confiqurations.add(commandConfiguration); + when(dtoFactory.createDto(CommandDto.class)).thenReturn(command); + when(command.withName(anyString())).thenReturn(command); + when(command.withCommandLine(anyString())).thenReturn(command); + when(command.withType(anyString())).thenReturn(command); + when(command.withAttributes(anyMap())).thenReturn(command); + when(view.getSelectedConfiguration()).thenReturn(commandConfiguration); + when(commandConfiguration.getType()).thenReturn(mock(CommandType.class)); + when(commandConfiguration.getName()).thenReturn(COMMAND_NAME); + + presenter.onEnterClicked(); + + verify(dtoFactory).createDto(CommandDto.class); + verify(workspaceServiceClient).updateCommand(eq(WORKSPACE_ID), eq(command)); + verify(workspacePromise).then(workspaceCaptor.capture()); + workspaceCaptor.getValue().apply(workspace); + + verify(view).setCancelButtonState(false); + verify(view).setSaveButtonState(false); + verify(workspaceServiceClient).getCommands(anyString()); + + verify(commandsPromise).then(commandsCaptor.capture()); + commandsCaptor.getValue().apply(commands); + + verify(commandConfigurationPromise).then(commandConfigurationCaptor.capture()); + commandConfigurationCaptor.getValue().apply(confiqurations); + + verify(view).setData(anyObject()); + verify(view).setFilterState(anyBoolean()); + verify(view).setCloseButtonInFocus(); + verify(view, never()).close(); + } +}