@Override
  public void onTreeNodeSelected(@NotNull ProcessTreeNode node) {
    selectedTreeNode = node;

    view.showProcessOutput(node.getId());
    resfreshStopButtonState(node.getId());
  }
  @Test
  public void shouldHideStopProcessButtonAtAddingCommand() throws Exception {
    ProcessTreeNode machineNode = mock(ProcessTreeNode.class);
    when(machineNode.getId()).thenReturn(MACHINE_ID);
    List<ProcessTreeNode> children = new ArrayList<>();
    children.add(machineNode);
    presenter.rootNode = new ProcessTreeNode(ROOT_NODE, null, null, null, children);

    ProcessTreeNode selectedCommandNode =
        new ProcessTreeNode(COMMAND_NODE, null, PROCESS_NAME, null, children);
    children.add(selectedCommandNode);

    when(outputConsole.isFinished()).thenReturn(true);

    presenter.consoles.clear();

    presenter.addCommandOutput(MACHINE_ID, outputConsole);

    verify(view).addProcessNode(anyObject());
    verify(view, never()).hideProcessOutput(anyString());

    verify(outputConsole).go(acceptsOneWidgetCaptor.capture());
    IsWidget widget = mock(IsWidget.class);
    acceptsOneWidgetCaptor.getValue().setWidget(widget);

    verify(view).addProcessWidget(anyString(), eq(widget));
    verify(view, times(2)).selectNode(anyObject());
    verify(view).setProcessesData(anyObject());
    verify(view).getNodeById(anyString());
    verify(view).setProcessRunning(anyString(), eq(false));
  }
  private void onStopProcess(@NotNull ProcessTreeNode node) {
    String processId = node.getId();
    ProcessTreeNode parentNode = node.getParent();

    int processIndex = view.getNodeIndex(processId);
    if (processIndex < 0) {
      return;
    }

    int countWidgets = terminals.size() + consoles.size();
    if (countWidgets == 1) {
      view.hideProcessOutput(processId);
      removeChildFromMachineNode(node, parentNode);
      return;
    }

    int neighborIndex = processIndex > 0 ? processIndex - 1 : processIndex + 1;
    ProcessTreeNode neighborNode = view.getNodeByIndex(neighborIndex);
    String neighborNodeId = neighborNode.getId();

    removeChildFromMachineNode(node, parentNode);
    view.selectNode(neighborNode);
    resfreshStopButtonState(neighborNodeId);
    view.showProcessOutput(neighborNodeId);
    view.hideProcessOutput(processId);
  }
  @Override
  public void onPreviewSsh(@NotNull final String machineId) {
    ProcessTreeNode machineTreeNode = findProcessTreeNodeById(machineId);
    if (machineTreeNode == null) {
      return;
    }

    MachineDto machine = (MachineDto) machineTreeNode.getData();

    OutputConsole defaultConsole = commandConsoleFactory.create("SSH");
    addCommandOutput(machineId, defaultConsole);

    String machineName = machine.getConfig().getName();
    String sshServiceAddress = getSshServerAddress(machine);
    String machineHost = "";
    String sshPort = SSH_PORT;
    if (sshServiceAddress != null) {
      String[] parts = sshServiceAddress.split(":");
      machineHost = parts[0];
      sshPort = (parts.length == 2) ? parts[1] : sshPort;
    }

    if (defaultConsole instanceof DefaultOutputConsole) {
      ((DefaultOutputConsole) defaultConsole)
          .printText(localizationConstant.sshConnectInfo(machineName, machineHost, sshPort));
    }
  }
 private ProcessTreeNode findProcessTreeNodeById(@NotNull String id) {
   for (ProcessTreeNode processTreeNode : rootNode.getChildren()) {
     if (id.equals(processTreeNode.getId())) {
       return processTreeNode;
     }
   }
   return null;
 }
 private boolean isTerminalNameExist(ProcessTreeNode machineNode, String terminalName) {
   for (ProcessTreeNode node : machineNode.getChildren()) {
     if (TERMINAL_NODE == node.getType() && node.getName().equals(terminalName)) {
       return true;
     }
   }
   return false;
 }
 private ProcessTreeNode getProcessTreeNodeByName(
     String processName, ProcessTreeNode machineTreeNode) {
   for (ProcessTreeNode processTreeNode : machineTreeNode.getChildren()) {
     if (processTreeNode.getName().equals(processName)) {
       return processTreeNode;
     }
   }
   return null;
 }
  @Test
  public void stopButtonStateShouldBeRefreshedWhenConsoleHasRunningProcess() {
    ProcessTreeNode commandNode = mock(ProcessTreeNode.class);
    when(commandNode.getId()).thenReturn(PROCESS_ID);

    when(outputConsole.isFinished()).thenReturn(false);
    presenter.consoles.put(PROCESS_ID, outputConsole);

    presenter.onTreeNodeSelected(commandNode);

    verify(view).setProcessRunning(PROCESS_ID, true);
  }
  @Override
  public void markProcessHasOutput(String processId) {
    if (processId.equals(activeProcessId)) {
      return;
    }

    ProcessTreeNode treeNode = processTreeNodes.get(processId);
    if (treeNode != null) {
      treeNode.setHasUnreadContent(true);
      treeNode.getTreeNodeElement().getClassList().add(machineResources.getCss().badgeVisible());
    }
  }
  @Test
  public void shouldShowCommanOutputWhenCommandSelected() throws Exception {
    ProcessTreeNode commandNode = mock(ProcessTreeNode.class);
    when(commandNode.getId()).thenReturn(PROCESS_ID);

    presenter.consoles.put(PROCESS_ID, outputConsole);

    presenter.onTreeNodeSelected(commandNode);

    verify(view).showProcessOutput(eq(PROCESS_ID));
    verify(view).setProcessRunning(anyString(), eq(true));
  }
  @Test
  public void shouldShowTerminalWhenTerminalNodeSelected() throws Exception {
    TerminalPresenter terminal = mock(TerminalPresenter.class);
    presenter.terminals.put(PROCESS_ID, terminal);

    ProcessTreeNode terminalNode = mock(ProcessTreeNode.class);
    when(terminalNode.getId()).thenReturn(PROCESS_ID);
    presenter.onTreeNodeSelected(terminalNode);

    verify(view).showProcessOutput(eq(PROCESS_ID));
    verify(view, never()).setProcessRunning(PROCESS_ID, true);
  }
  @Override
  public int getNodeIndex(String processId) {
    int index = 0;
    for (ProcessTreeNode processTreeNode : processTreeNodes.values()) {
      if (processTreeNode.getId().equals(processId)) {
        return index;
      }

      index++;
    }

    return -1;
  }
  @Override
  public void setProcessesData(@NotNull ProcessTreeNode root) {
    processTree.asWidget().setVisible(true);
    processTree.getModel().setRoot(root);
    processTree.renderTree(-1);

    for (ProcessTreeNode processTreeNode : processTreeNodes.values()) {
      if (!processTreeNode.getId().equals(activeProcessId) && processTreeNode.hasUnreadContent()) {
        processTreeNode
            .getTreeNodeElement()
            .getClassList()
            .add(machineResources.getCss().badgeVisible());
      }
    }
  }
  @Override
  public void onCloseCommandOutputClick(@NotNull ProcessTreeNode node) {
    String commandId = node.getId();
    OutputConsole console = consoles.get(commandId);

    if (console == null) {
      return;
    }

    if (console.isFinished()) {
      console.close();
      onStopProcess(node);
      consoles.remove(commandId);
      consoleCommands.remove(console);
      return;
    }

    dialogFactory
        .createConfirmDialog(
            "",
            localizationConstant.outputsConsoleViewStopProcessConfirmation(console.getTitle()),
            getConfirmCloseConsoleCallback(console, node),
            null)
        .show();
  }
 @Override
 public void onStopCommandProcess(@NotNull ProcessTreeNode node) {
   String commandId = node.getId();
   if (consoles.containsKey(commandId) && !consoles.get(commandId).isFinished()) {
     consoles.get(commandId).stop();
   }
 }
  @Override
  public void setProcessRunning(String nodeId, boolean running) {
    ProcessTreeNode processTreeNode = processTreeNodes.get(nodeId);
    if (processTreeNode == null) {
      return;
    }

    processTreeNode.setRunning(running);

    JsElement spanElement =
        (JsElement) processTreeNode.getTreeNodeElement().getFirstChild().getChildNodes().item(1);
    if (running) {
      spanElement.setAttribute("running", "true");
    } else {
      spanElement.setAttribute("running", "false");
    }
  }
  @Test
  public void shouldStopProcessWithoutCloseCommanOutput() throws Exception {
    ProcessTreeNode machineNode = mock(ProcessTreeNode.class);
    ProcessTreeNode commandNode = mock(ProcessTreeNode.class);
    when(machineNode.getId()).thenReturn(MACHINE_ID);
    List<ProcessTreeNode> children = new ArrayList<>();
    children.add(machineNode);
    presenter.rootNode = new ProcessTreeNode(ROOT_NODE, null, null, null, children);

    when(outputConsole.isFinished()).thenReturn(false);
    presenter.consoles.put(PROCESS_ID, outputConsole);
    //noinspection ConstantConditions
    machineNode.getChildren().add(commandNode);

    when(commandNode.getId()).thenReturn(PROCESS_ID);
    when(view.getNodeIndex(anyString())).thenReturn(0);
    when(machineNode.getChildren()).thenReturn(children);
    when(commandNode.getParent()).thenReturn(machineNode);

    presenter.onStopCommandProcess(commandNode);

    verify(outputConsole).stop();
    verify(view, never()).hideProcessOutput(eq(PROCESS_ID));
    verify(view, never()).removeProcessNode(eq(commandNode));
  }
  @Test
  public void shouldCloseCommanOutputWhenCommandHasFinished() throws Exception {
    ProcessTreeNode machineNode = mock(ProcessTreeNode.class);
    ProcessTreeNode commandNode = mock(ProcessTreeNode.class);
    when(machineNode.getId()).thenReturn(MACHINE_ID);
    List<ProcessTreeNode> children = new ArrayList<>();
    children.add(machineNode);
    presenter.rootNode = new ProcessTreeNode(ROOT_NODE, null, null, null, children);

    when(outputConsole.isFinished()).thenReturn(true);
    presenter.consoles.put(PROCESS_ID, outputConsole);
    machineNode.getChildren().add(commandNode);

    when(commandNode.getId()).thenReturn(PROCESS_ID);
    when(view.getNodeIndex(anyString())).thenReturn(0);
    when(machineNode.getChildren()).thenReturn(children);
    when(commandNode.getParent()).thenReturn(machineNode);

    presenter.onCloseCommandOutputClick(commandNode);

    verify(commandNode, times(2)).getId();
    verify(commandNode).getParent();
    verify(view).getNodeIndex(eq(PROCESS_ID));
    verify(view).hideProcessOutput(eq(PROCESS_ID));
    verify(view).removeProcessNode(eq(commandNode));
    verify(view).setProcessesData(anyObject());
  }
 @Override
 public void onCloseTerminal(@NotNull ProcessTreeNode node) {
   String terminalId = node.getId();
   if (terminals.containsKey(terminalId)) {
     onStopProcess(node);
     terminals.get(terminalId).stopTerminal();
     terminals.remove(terminalId);
   }
 }
  @Override
  public void showProcessOutput(String processId) {
    if (processWidgets.containsKey(processId)) {
      onResize();
      outputPanel.showWidget(processWidgets.get(processId).asWidget());

      activeProcessId = processId;

      ProcessTreeNode treeNode = processTreeNodes.get(processId);
      if (treeNode != null) {
        treeNode.setHasUnreadContent(false);
        treeNode
            .getTreeNodeElement()
            .getClassList()
            .remove(machineResources.getCss().badgeVisible());
      }
    }
  }
  /** Opens new terminal for the selected machine. */
  public void newTerminal() {
    workspaceAgent.setActivePart(this);

    if (selectedTreeNode == null) {
      if (appContext.getDevMachine() != null) {
        onAddTerminal(appContext.getDevMachine().getId());
      }
      return;
    }

    if (selectedTreeNode.getType() == MACHINE_NODE) {
      onAddTerminal(selectedTreeNode.getId());
    } else {
      if (selectedTreeNode.getParent() != null
          && selectedTreeNode.getParent().getType() == MACHINE_NODE) {
        onAddTerminal(appContext.getDevMachine().getId());
      }
    }
  }
  private void addMachineToConsoles(MachineDto machine) {
    List<ProcessTreeNode> processTreeNodes = new ArrayList<ProcessTreeNode>();
    ProcessTreeNode machineNode =
        new ProcessTreeNode(MACHINE_NODE, rootNode, machine, null, processTreeNodes);
    machineNode.setRunning(true);

    if (rootChildren.contains(machineNode)) {
      rootChildren.remove(machineNode);
    }

    rootChildren.add(machineNode);
    view.setProcessesData(rootNode);

    String machineId = machine.getId();

    restoreState(machineId);

    machineNodes.put(machineId, machineNode);
  }
  @Override
  public void setStopButtonVisibility(String nodeId, boolean visible) {
    ProcessTreeNode processTreeNode = processTreeNodes.get(nodeId);
    if (processTreeNode == null) {
      return;
    }

    if (visible) {
      processTreeNode
          .getTreeNodeElement()
          .getClassList()
          .remove(machineResources.getCss().hideStopButton());
    } else {
      processTreeNode
          .getTreeNodeElement()
          .getClassList()
          .add(machineResources.getCss().hideStopButton());
    }
  }
  @Test
  public void shouldShowConfirmDialogWhenCommandHasNotFinished() throws Exception {
    ConfirmDialog confirmDialog = mock(ConfirmDialog.class);
    ProcessTreeNode commandNode = mock(ProcessTreeNode.class);

    when(outputConsole.isFinished()).thenReturn(false);
    presenter.consoles.put(PROCESS_ID, outputConsole);

    when(commandNode.getId()).thenReturn(PROCESS_ID);
    when(dialogFactory.createConfirmDialog(anyString(), anyString(), anyObject(), anyObject()))
        .thenReturn(confirmDialog);

    presenter.onCloseCommandOutputClick(commandNode);

    verify(commandNode).getId();
    verify(view, never()).hideProcessOutput(anyString());
    verify(view, never()).removeProcessNode(anyObject());
    verify(dialogFactory).createConfirmDialog(anyString(), anyString(), anyObject(), anyObject());
    verify(confirmDialog).show();
  }
  @Test
  public void shouldCloseTerminal() throws Exception {
    TerminalPresenter terminal = mock(TerminalPresenter.class);
    ProcessTreeNode machineNode = mock(ProcessTreeNode.class);
    ProcessTreeNode terminalNode = mock(ProcessTreeNode.class);
    when(machineNode.getId()).thenReturn(MACHINE_ID);
    List<ProcessTreeNode> children = new ArrayList<>();
    children.add(machineNode);
    presenter.rootNode = new ProcessTreeNode(ROOT_NODE, null, null, null, children);
    presenter.terminals.put(PROCESS_ID, terminal);

    when(terminalNode.getId()).thenReturn(PROCESS_ID);
    when(view.getNodeIndex(anyString())).thenReturn(0);
    when(machineNode.getChildren()).thenReturn(children);
    when(terminalNode.getParent()).thenReturn(machineNode);

    presenter.onCloseTerminal(terminalNode);

    verify(terminal).stopTerminal();
    verify(terminalNode, times(2)).getId();
    verify(terminalNode).getParent();
    verify(view).getNodeIndex(eq(PROCESS_ID));
    verify(view).hideProcessOutput(eq(PROCESS_ID));
    verify(view).removeProcessNode(eq(terminalNode));
    verify(view).setProcessesData(anyObject());
  }
  @Test
  public void shouldShowStopProcessButtonAtAddingTerminal() throws Exception {
    MachineDto machineDto = mock(MachineDto.class);
    MachineConfigDto machineConfigDto = mock(MachineConfigDto.class);
    when(machineDto.getConfig()).thenReturn(machineConfigDto);
    when(machineConfigDto.isDev()).thenReturn(true);
    when(machineDto.getStatus()).thenReturn(MachineStatus.RUNNING);

    ProcessTreeNode machineNode = mock(ProcessTreeNode.class);
    when(machineNode.getId()).thenReturn(MACHINE_ID);
    List<ProcessTreeNode> children = new ArrayList<>();
    children.add(machineNode);
    presenter.rootNode = new ProcessTreeNode(ROOT_NODE, null, null, null, children);

    Machine machine = mock(Machine.class);
    when(entityFactory.createMachine(anyObject())).thenReturn(machine);
    TerminalPresenter terminal = mock(TerminalPresenter.class);
    when(terminalFactory.create(machine)).thenReturn(terminal);
    IsWidget terminalWidget = mock(IsWidget.class);
    when(terminal.getView()).thenReturn(terminalWidget);

    presenter.addCommandOutput(MACHINE_ID, outputConsole);
    presenter.onAddTerminal(MACHINE_ID);

    verify(machinePromise).then(machineCaptor.capture());
    machineCaptor.getValue().apply(machineDto);

    verify(entityFactory).createMachine(anyObject());
    verify(terminalFactory).create(eq(machine));
    verify(terminal).getView();
    verify(view, times(2)).setProcessesData(anyObject());
    verify(view, times(2)).selectNode(anyObject());
    verify(view).addProcessWidget(anyString(), eq(terminalWidget));
    verify(view, times(2)).addProcessNode(anyObject());
    verify(terminal).setVisible(eq(true));
    verify(terminal).connect();
    verify(terminal).setListener(anyObject());
    verify(view).setProcessRunning(anyString(), eq(true));
  }
  @Test
  public void shouldAddCommand() throws Exception {
    ProcessTreeNode machineNode = mock(ProcessTreeNode.class);
    when(machineNode.getId()).thenReturn(MACHINE_ID);
    List<ProcessTreeNode> children = new ArrayList<>();
    children.add(machineNode);
    presenter.rootNode = new ProcessTreeNode(ROOT_NODE, null, null, null, children);

    presenter.addCommandOutput(MACHINE_ID, outputConsole);

    verify(view).addProcessNode(anyObject());
    verify(view, never()).hideProcessOutput(anyString());

    verify(outputConsole).go(acceptsOneWidgetCaptor.capture());
    IsWidget widget = mock(IsWidget.class);
    acceptsOneWidgetCaptor.getValue().setWidget(widget);

    verify(view).addProcessWidget(anyString(), eq(widget));
    verify(view, times(2)).selectNode(anyObject());
    verify(view).setProcessesData(anyObject());
    verify(view).getNodeById(anyString());
    verify(view).setProcessRunning(anyString(), anyBoolean());
  }
  @Test
  public void shouldReplaceCommandOutput() throws Exception {
    MachineDto machineDto = mock(MachineDto.class);
    when(machineDto.getId()).thenReturn(MACHINE_ID);
    MachineConfigDto machineConfigDto = mock(MachineConfigDto.class);
    when(machineDto.getConfig()).thenReturn(machineConfigDto);

    List<ProcessTreeNode> children = new ArrayList<>();
    ProcessTreeNode commandNode =
        new ProcessTreeNode(COMMAND_NODE, null, PROCESS_NAME, null, children);
    children.add(commandNode);
    ProcessTreeNode machineNode =
        new ProcessTreeNode(MACHINE_NODE, null, machineDto, null, children);
    children.add(machineNode);
    when(machineNode.getId()).thenReturn(MACHINE_ID);

    String commandId = commandNode.getId();
    presenter.rootNode = new ProcessTreeNode(ROOT_NODE, null, null, null, children);
    presenter.consoles.put(commandId, outputConsole);

    when(outputConsole.isFinished()).thenReturn(true);
    when(outputConsole.getTitle()).thenReturn(PROCESS_NAME);

    presenter.addCommandOutput(MACHINE_ID, outputConsole);

    verify(view, never()).addProcessNode(anyObject());
    verify(view, never()).setProcessesData(anyObject());

    verify(outputConsole).go(acceptsOneWidgetCaptor.capture());
    IsWidget widget = mock(IsWidget.class);
    acceptsOneWidgetCaptor.getValue().setWidget(widget);

    verify(view).hideProcessOutput(eq(commandId));
    verify(view).addProcessWidget(eq(commandId), eq(widget));
    verify(view).selectNode(anyObject());
    verify(view).getNodeById(eq(commandId));
  }
  /**
   * Adds command node to process tree and displays command output
   *
   * @param machineId id of machine in which the command will be executed
   * @param outputConsole the console for command output
   */
  public void addCommandOutput(@NotNull String machineId, @NotNull OutputConsole outputConsole) {
    ProcessTreeNode machineTreeNode = findProcessTreeNodeById(machineId);
    if (machineTreeNode == null) {
      notificationManager.notify(
          localizationConstant.failedToExecuteCommand(),
          localizationConstant.machineNotFound(machineId),
          FAIL,
          FLOAT_MODE);
      Log.error(getClass(), localizationConstant.machineNotFound(machineId));
      return;
    }

    String commandId;
    String outputConsoleTitle = outputConsole.getTitle();
    ProcessTreeNode processTreeNode = getProcessTreeNodeByName(outputConsoleTitle, machineTreeNode);
    if (processTreeNode != null && isCommandStopped(processTreeNode.getId())) {
      commandId = processTreeNode.getId();
      view.hideProcessOutput(commandId);
    } else {
      ProcessTreeNode commandNode =
          new ProcessTreeNode(
              COMMAND_NODE,
              machineTreeNode,
              outputConsoleTitle,
              outputConsole.getTitleIcon(),
              null);
      commandId = commandNode.getId();
      view.addProcessNode(commandNode);
      addChildToMachineNode(commandNode, machineTreeNode);
    }

    updateCommandOutput(commandId, outputConsole);

    resfreshStopButtonState(commandId);
    workspaceAgent.setActivePart(this);
  }
  @Override
  public void onWorkspaceStopped(WorkspaceStoppedEvent event) {
    for (ProcessTreeNode processTreeNode : rootNode.getChildren()) {
      if (processTreeNode.getType() == MACHINE_NODE) {
        onCloseTerminal(processTreeNode);
        processTreeNode.setRunning(false);
        if (processTreeNode.getChildren() != null) {
          processTreeNode.getChildren().clear();
        }
      }
    }

    rootNode.getChildren().clear();
    rootChildren.clear();

    view.clear();
    view.selectNode(null);
    view.setProcessesData(rootNode);
  }