Merge pull request #534 from eclipse/CHE-359

CHE-359: Guide the user while connecting the remote debugger
6.19.x
Anatoliy Bazko 2016-02-29 15:32:11 +02:00
commit bceeb307af
7 changed files with 662 additions and 23 deletions

View File

@ -0,0 +1,494 @@
/*******************************************************************************
* 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.ui.listbox;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.LabelElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.RootPanel;
/**
* ComboBox widget.
*
* @author Oleksii Orel
* @author Anatoliy Bazko
*/
public class CustomComboBox extends FocusWidget implements HasChangeHandlers {
private static final CustomListBoxResources RESOURCES = GWT.create(CustomListBoxResources.class);
static {
RESOURCES.getCSS().ensureInjected();
}
private final InputElement currentInputElement = Document.get().createTextInputElement();
private final FlowPanel optionsPanel = new FlowPanel();
private final String optionsGroupName = "comboBox-" + Document.get().createUniqueId();
private int dropDownSize = 6;
private int selectedIndex = -1;
private int defaultSelectedIndex = -1;
private boolean isActive = false;
public static CustomComboBox wrap(Element element) {
// Assert that the element is attached.
assert Document.get().getBody().isOrHasChild(element);
CustomComboBox customComboBox = new CustomComboBox(element);
// Mark it attached and remember it for cleanup.
customComboBox.onAttach();
RootPanel.detachOnWindowClose(customComboBox);
return customComboBox;
}
/**
* Creates an empty custom list box.
*/
public CustomComboBox() {
super(Document.get().createDivElement());
Element comboBoxElement = getElement();
comboBoxElement.appendChild(currentInputElement);
comboBoxElement.appendChild(optionsPanel.getElement());
comboBoxElement.appendChild(RESOURCES.arrow().getSvg().getElement());
optionsPanel.setVisible(false);
addDomHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
optionsPanel.setVisible(false);
isActive = false;
}
}, BlurEvent.getType());
addDomHandler(new MouseDownHandler() {
@Override
public void onMouseDown(MouseDownEvent event) {
//Update isActive state. It actually when we lose onBlur event in the parent widget.
isActive = isActive(getElement());
if (!isActive) {
optionsPanel.setVisible(true);
}
}
}, MouseDownEvent.getType());
addDomHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
if (isActive) {
optionsPanel.setVisible(!optionsPanel.isVisible());
} else {
isActive = true;
}
}
}, MouseUpEvent.getType());
addDomHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent event) {
resetSelectedIndex();
}
}, KeyDownEvent.getType());
addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
NodeList<Element> selectionElements = optionsPanel.getElement().getElementsByTagName("input");
for (int pos = 0; pos < selectionElements.getLength(); pos++) {
InputElement inputElement = (InputElement)selectionElements.getItem(pos);
if (inputElement.isChecked()) {
currentInputElement.setValue(getItemText(pos));
selectedIndex = pos;
break;
}
}
}
});
setStyleName(RESOURCES.getCSS().listBox());
}
/**
* This constructor may be used by subclasses to explicitly use an existing element.
*
* @param element
* the element to be used
*/
protected CustomComboBox(Element element) {
super(element);
}
private InputElement getListItemElement(int index) {
final Element optionElement = (Element)optionsPanel.getElement().getChild(index);
return (InputElement)optionElement.getElementsByTagName("input").getItem(0);
}
/**
* Adds an ChangeHandler.
*
* @param handler
* the change handler
*/
public HandlerRegistration addChangeHandler(ChangeHandler handler) {
return addDomHandler(handler, ChangeEvent.getType());
}
/**
* Adds an item to the list box.
*
* @param item
* the text of the item to be added
*/
public void addItem(String item) {
this.insertItem(item);
}
/**
* Adds an item to the list box, specifying an initial value for the item.
*
* @param item
* the text of the item to be added
* @param value
* the item's value, to be submitted if it is part of a
* {@link com.google.gwt.user.client.ui.FormPanel}; cannot be <code>null</code>
*/
public void addItem(String item, String value) {
this.insertItem(item, value);
}
/**
* Removes all items from the list box.
*/
public void clear() {
selectedIndex = -1;
optionsPanel.getElement().removeAllChildren();
}
/**
* Gets the number of items present in the list box.
*
* @return the number of items
*/
public int getItemCount() {
return optionsPanel.getElement().getChildCount();
}
/**
* Gets the text associated with the item at the specified index.
*
* @param index
* the index of the item whose text is to be retrieved
* @return the text associated with the item
* @throws IndexOutOfBoundsException
* if the index is out of range
*/
public String getItemText(int index) {
checkIndex(index);
final Element optionElement = (Element)optionsPanel.getElement().getChild(index);
final InputElement labelElement = (InputElement)optionElement.getElementsByTagName("input").getItem(0);
return labelElement.getValue();
}
/**
* Gets the text for currently selected item. If multiple items are selected,
* this method will return the text of the first selected item.
*
* @return the text for selected item, or {@code null} if none is selected
*/
public String getSelectedItemText() {
int index = getSelectedIndex();
return index == -1 ? null : getItemText(index);
}
/**
* Gets the currently-selected item.
*
* @return the selected index, or <code>-1</code> if none is selected
*/
public int getSelectedIndex() {
return selectedIndex;
}
/**
* Gets the value associated with the item at a given index.
*
* @param index
* the index of the item to be retrieved
* @return the item's associated value
* @throws IndexOutOfBoundsException
* if the index is out of range
*/
public String getValue(int index) {
checkIndex(index);
final Element optionElement = (Element)optionsPanel.getElement().getChild(index);
final InputElement inputElement = (InputElement)optionElement.getElementsByTagName("input").getItem(0);
return inputElement.getValue();
}
/**
* Gets the value for currently selected item.
*
* @return the value for selected item, or {@code null} if none is selected
*/
public String getValue() {
return currentInputElement.getValue();
}
/**
* Inserts an item into the custom list box.
*
* @param item
* the text of the item to be inserted
*/
public void insertItem(String item) {
this.insertItem(item, item);
}
/**
* Inserts an item into the list box.
*
* @param item
* the text of the item to be inserted.
* @param value
* the item's value.
*/
public void insertItem(String item, String value) {
//create new widget
final RadioButton radioButton = new RadioButton(optionsGroupName, item);
//remove the default gwt-RadioButton style
radioButton.removeStyleName("gwt-RadioButton");
//set value
final InputElement inputElement = (InputElement)radioButton.getElement().getElementsByTagName("input").getItem(0);
inputElement.removeAttribute("tabindex");
inputElement.setAttribute("value", value);
//set default state
if (defaultSelectedIndex > -1 && optionsPanel.getElement().getChildCount() == defaultSelectedIndex) {
inputElement.setChecked(true);
currentInputElement.setValue("");
}
//add to widget
optionsPanel.add(radioButton);
}
/**
* Sets custom height inside widget as height and line-height properties.
*
* @param height
*/
public void setHeight(String height) {
this.getElement().getStyle().setProperty("height", height);
currentInputElement.getStyle().setProperty("lineHeight", height);
optionsPanel.getElement().getStyle().setProperty("lineHeight", height);
optionsPanel.getElement().getStyle().setProperty("maxHeight", "calc(" + dropDownSize + "*" + height + ")");
}
/**
* Sets dropdown part size.
*
* @param dropDownSize
* @throws IndexOutOfBoundsException
* if the index is out of range
*/
public void setSize(int dropDownSize) {
if (dropDownSize < 1 || dropDownSize > 99) {
throw new IndexOutOfBoundsException();
}
this.dropDownSize = dropDownSize;
}
/**
* Determines whether an individual list item is selected.
*
* @param index
* the index of the item to be tested
* @return <code>true</code> if the item is selected
* @throws IndexOutOfBoundsException
* if the index is out of range
*/
public boolean isItemSelected(int index) {
checkIndex(index);
return selectedIndex == index;
}
/**
* Removes the item at the specified index.
*
* @param index
* the index of the item to be removed
* @throws IndexOutOfBoundsException
* if the index is out of range
*/
public void removeItem(int index) {
checkIndex(index);
if (index == selectedIndex) {
currentInputElement.setValue("");
selectedIndex = -1;
}
optionsPanel.getElement().removeChild(optionsPanel.getElement().getChild(index));
}
/**
* Sets whether an individual list item is selected.
*
* @param index
* the index of the item to be selected or unselected
* @param selected
* <code>true</code> to select the item
*/
public void setItemSelected(int index, boolean selected) {
if (index < 0 || index >= getItemCount()) {
return;
}
if (selected) {
selectedIndex = index;
currentInputElement.setValue(getItemText(index));
}
final InputElement inputElement = getListItemElement(index);
inputElement.setChecked(selected);
}
/**
* Sets the text associated with the item at a given index.
*
* @param index
* the index of the item to be set
* @param text
* the item's new text
* @throws IndexOutOfBoundsException
* if the index is out of range
*/
public void setItemText(int index, String text) {
checkIndex(index);
final Element optionElement = (Element)optionsPanel.getElement().getChild(index);
final LabelElement labelElement = (LabelElement)optionElement.getElementsByTagName("label").getItem(0);
labelElement.setInnerText(text);
if (selectedIndex == index) {
currentInputElement.setValue(text);
}
}
/**
* Sets the currently selected index.
*
* @param index
* the index of the item to be selected
*/
public void setSelectedIndex(int index) {
if (index < 0) {
return;
}
//set default index if not added options yet
if (index >= getItemCount()) {
defaultSelectedIndex = index;
return;
}
selectedIndex = index;
currentInputElement.setValue(getItemText(index));
final InputElement inputElement = getListItemElement(index);
inputElement.setChecked(true);
}
/**
* Sets the value associated with the item at a given index.
*
* @param index
* the index of the item to be set
* @param value
* the item's new value
* @throws IndexOutOfBoundsException
* if the index is out of range
*/
public void setValue(int index, String value) {
checkIndex(index);
final InputElement inputElement = getListItemElement(index);
inputElement.setValue(value);
}
/**
* Sets the value without association to any specific index.
*
* @param value
* the new value
*/
public void setValue(String value) {
resetSelectedIndex();
currentInputElement.setValue(value);
}
/**
* @see com.google.gwt.user.client.ui.UIObject#onEnsureDebugId(String)
*/
@Override
protected void onEnsureDebugId(String baseID) {
super.onEnsureDebugId(baseID);
}
private void checkIndex(int index) {
if (index < 0 || index >= getItemCount()) {
throw new IndexOutOfBoundsException();
}
}
/**
* Ensures that no item is selected.
*/
private void resetSelectedIndex() {
if (getSelectedIndex() != -1) {
selectedIndex = -1;
NodeList<Element> selectionElements = optionsPanel.getElement().getElementsByTagName("input");
for (int pos = 0; pos < selectionElements.getLength(); pos++) {
InputElement inputElement = (InputElement)selectionElements.getItem(pos);
inputElement.setChecked(false);
}
}
}
@Override
public void setFocus(boolean focused) {
currentInputElement.focus();
}
/**
* Check isActive status.
*/
private native boolean isActive(Element element) /*-{
var activeElement = $doc.activeElement;
return activeElement.isEqualNode(element);
}-*/;
}

View File

@ -35,6 +35,21 @@
display: inline;
}
.listBox input {
font-size: inherit;
font-weight: inherit !important;
vertical-align: bottom;
white-space: nowrap;
color: listBoxColor;
background-color: textFieldBackgroundColor;
padding-left: 8px;
display: inline;
border: none;
height: 100%;
width: literal("calc(100% - 32px)");
outline: none;
}
.listBox > label {
margin-right: 22px;
line-height: 22px;

View File

@ -10,13 +10,24 @@
*******************************************************************************/
package org.eclipse.che.ide.ext.java.jdi.client.debug.remotedebug;
import com.google.gwt.user.client.Timer;
import com.google.inject.Inject;
import org.eclipse.che.api.machine.gwt.client.MachineServiceClient;
import org.eclipse.che.api.machine.shared.dto.MachineDto;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.ext.java.jdi.client.debug.DebuggerPresenter;
import com.google.gwt.user.client.Timer;
import org.eclipse.che.ide.extension.machine.client.inject.factories.EntityFactory;
import org.eclipse.che.ide.extension.machine.client.machine.Machine;
import org.eclipse.che.ide.extension.machine.client.perspective.widgets.machine.appliance.server.Server;
import org.eclipse.che.ide.util.Pair;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Contains methods which allows control of remote debugging.
@ -25,12 +36,22 @@ import javax.validation.constraints.NotNull;
*/
public class RemoteDebugPresenter implements RemoteDebugView.ActionDelegate {
private final RemoteDebugView view;
private final DebuggerPresenter debuggerPresenter;
private final RemoteDebugView view;
private final DebuggerPresenter debuggerPresenter;
private final AppContext appContext;
private final MachineServiceClient machineServiceClient;
private final EntityFactory entityFactory;
@Inject
public RemoteDebugPresenter(RemoteDebugView view, DebuggerPresenter debuggerPresenter) {
public RemoteDebugPresenter(RemoteDebugView view,
DebuggerPresenter debuggerPresenter,
AppContext appContext,
MachineServiceClient machineServiceClient,
EntityFactory entityFactory) {
this.view = view;
this.appContext = appContext;
this.machineServiceClient = machineServiceClient;
this.entityFactory = entityFactory;
this.view.setDelegate(this);
this.debuggerPresenter = debuggerPresenter;
@ -39,15 +60,44 @@ public class RemoteDebugPresenter implements RemoteDebugView.ActionDelegate {
/** Calls special method on view which shows dialog window. */
public void showDialog() {
view.show();
setPortsList();
// TODO fix behaviour. Because of animation/render we cannot set focus without delay
new Timer() {
@Override
public void run() {
view.setFocusInHostField();
view.focus();
}
}.schedule(300);
}
private void setPortsList() {
machineServiceClient.getMachine(appContext.getDevMachineId()).then(new Operation<MachineDto>() {
@Override
public void apply(MachineDto machineDto) throws OperationException {
Machine machine = entityFactory.createMachine(machineDto);
List<Pair<String, String>> ports = extractPortsList(machine);
view.setPortsList(ports);
}
});
}
/**
* Extracts list of ports available to connect by debugger.
*/
private List<Pair<String, String>> extractPortsList(Machine machine) {
List<Pair<String, String>> ports = new ArrayList<Pair<String, String>>();
for (Server server : machine.getServersList()) {
String description = server.getPort() + " (" + server.getRef() + ")";
String value = server.getPort();
Pair<String, String> pair = new Pair<>(description, value);
ports.add(pair);
}
return ports;
}
/** {@inheritDoc} */
@Override
public void onConfirmClicked(@NotNull String host, @Min(1) int port) {

View File

@ -13,9 +13,11 @@ package org.eclipse.che.ide.ext.java.jdi.client.debug.remotedebug;
import com.google.inject.ImplementedBy;
import org.eclipse.che.ide.api.mvp.View;
import org.eclipse.che.ide.util.Pair;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* Provides methods which allow control of remote debugging.
@ -28,6 +30,17 @@ public interface RemoteDebugView extends View<RemoteDebugView.ActionDelegate> {
/** Shows dialog window to connect debugger to remote server */
void show();
/** Sets focus to either host input field or port input field if first one is disabled */
void focus();
/**
* Sets list of ports to help user to choose the appropriate one.
*
* @param ports
* the ports
*/
void setPortsList(@NotNull List<Pair<String, String>> ports);
interface ActionDelegate {
/**
* Performs some actions when user clicks on confirm button and input host and port.
@ -40,7 +53,4 @@ public interface RemoteDebugView extends View<RemoteDebugView.ActionDelegate> {
void onConfirmClicked(@NotNull String host, @Min(1) int port);
}
/** Give focus to host field. */
void setFocusInHostField();
}

View File

@ -10,9 +10,13 @@
*******************************************************************************/
package org.eclipse.che.ide.ext.java.jdi.client.debug.remotedebug;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.TextBox;
@ -26,13 +30,18 @@ import org.eclipse.che.ide.ui.dialogs.CancelCallback;
import org.eclipse.che.ide.ui.dialogs.ConfirmCallback;
import org.eclipse.che.ide.ui.dialogs.DialogFactory;
import org.eclipse.che.ide.ui.dialogs.confirm.ConfirmDialog;
import org.eclipse.che.ide.ui.listbox.CustomComboBox;
import org.eclipse.che.ide.util.Pair;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
/**
* @author Dmitry Shnurenko
* @author Anatoliy Bazko
*/
public class RemoteDebugViewImpl extends Composite implements RemoteDebugView {
interface RemoteDebugImplUiBinder extends UiBinder<Widget, RemoteDebugViewImpl> {
@ -40,26 +49,29 @@ public class RemoteDebugViewImpl extends Composite implements RemoteDebugView {
private static final RemoteDebugImplUiBinder UI_BINDER = GWT.create(RemoteDebugImplUiBinder.class);
@UiField
CheckBox devHost;
@UiField
DockLayoutPanel mainPanel;
@UiField
TextBox host;
@UiField
TextBox port;
CustomComboBox port;
@UiField(provided = true)
final JavaRuntimeLocalizationConstant locale;
@UiField(provided = true)
final JavaRuntimeResources resources;
private ActionDelegate delegate;
private ActionDelegate delegate;
private List<Pair<String, String>> ports;
private final ConfirmDialog dialog;
@Inject
public RemoteDebugViewImpl(final JavaRuntimeLocalizationConstant locale,
JavaRuntimeResources resources,
DialogFactory dialogFactory,
final JavaRuntimeResources resources,
final DialogFactory dialogFactory,
final NotificationManager notificationManager) {
this.locale = locale;
this.resources = resources;
@ -70,7 +82,7 @@ public class RemoteDebugViewImpl extends Composite implements RemoteDebugView {
@Override
public void accepted() {
try {
delegate.onConfirmClicked(host.getText(), Integer.parseInt(port.getText()));
delegate.onConfirmClicked(host.getValue(), Integer.parseInt(port.getValue()));
} catch (NumberFormatException exception) {
dialog.show();
notificationManager.notify(locale.failedToConnectToRemoteDebuggerTitle(),
@ -86,7 +98,21 @@ public class RemoteDebugViewImpl extends Composite implements RemoteDebugView {
}
};
bind();
this.dialog = dialogFactory.createConfirmDialog(locale.connectToRemote(), this, confirmCallback, cancelCallback);
this.devHost.setValue(true);
this.ports = new ArrayList<Pair<String, String>>();
updateDialog();
}
/** Bind handlers. */
private void bind() {
devHost.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
updateDialog();
}
});
}
/** {@inheritDoc} */
@ -102,8 +128,40 @@ public class RemoteDebugViewImpl extends Composite implements RemoteDebugView {
}
@Override
public void setFocusInHostField() {
host.setFocus(true);
public void setPortsList(@NotNull List<Pair<String, String>> ports) {
this.ports = ports;
updatePortsList();
}
private void updatePortsList() {
port.clear();
for (Pair<String, String> entry : ports) {
port.addItem(entry.first, entry.second);
}
}
private void updateDialog() {
boolean connectToDevMachine = devHost.getValue();
host.setEnabled(!connectToDevMachine);
if (connectToDevMachine) {
host.setValue("localhost");
updatePortsList();
port.setFocus(true);
} else {
host.selectAll();
host.setFocus(true);
port.clear();
}
}
@Override
public void focus() {
if (host.isEnabled()) {
host.setFocus(true);
} else {
port.setFocus(true);
}
}
}

View File

@ -11,7 +11,8 @@
-->
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:che='urn:import:org.eclipse.che.ide.ui.listbox'>
<ui:with field='locale' type='org.eclipse.che.ide.ext.java.jdi.client.JavaRuntimeLocalizationConstant'/>
<ui:with field='resources' type='org.eclipse.che.ide.ext.java.jdi.client.JavaRuntimeResources'/>
@ -29,12 +30,17 @@
.hostTextBox {
float: left;
margin-left: 12px;
width: 158px;
}
.portTextBox {
.portComboBox {
float: left;
margin-left: 14px;
width: 160px;
}
.mainPanel > div {
overflow: visible !important;
}
</ui:style>
@ -45,6 +51,12 @@
</g:FlowPanel>
</g:north>
<g:north size="40">
<g:FlowPanel>
<g:CheckBox ui:field="devHost" text="Connect to process on dev machine"/>
</g:FlowPanel>
</g:north>
<g:north size="40">
<g:FlowPanel>
<g:Label text="{locale.host}" addStyleNames="{style.label}"/>
@ -55,7 +67,7 @@
<g:north size="40">
<g:FlowPanel>
<g:Label text="{locale.port}" addStyleNames="{style.label}"/>
<g:TextBox ui:field="port" addStyleNames="{style.portTextBox}"/>
<che:CustomComboBox ui:field="port" addStyleNames="{style.portComboBox}"/>
</g:FlowPanel>
</g:north>
</g:DockLayoutPanel>

View File

@ -76,8 +76,8 @@ public class RemoteDebugViewImplTest {
@Test
public void confirmAcceptedShouldBeCalled() throws Exception {
when(view.host.getText()).thenReturn(SOME_TEXT);
when(view.port.getText()).thenReturn("8000");
when(view.host.getValue()).thenReturn(SOME_TEXT);
when(view.port.getValue()).thenReturn("8000");
verify(dialogFactory).createConfirmDialog(eq(SOME_TEXT),
eq(view),
confirmCallbackCaptor.capture(),
@ -86,8 +86,8 @@ public class RemoteDebugViewImplTest {
confirmCallbackCaptor.getValue().accepted();
verify(delegate).onConfirmClicked(SOME_TEXT, 8000);
verify(view.host).getText();
verify(view.port).getText();
verify(view.host).getValue();
verify(view.port).getValue();
verify(locale).connectToRemote();
}