@Test
  public void testAddOnPrivateChatMessageListener() throws Exception {
    CompletableFuture<String> usernameFuture = new CompletableFuture<>();
    CompletableFuture<ChatMessage> chatMessageFuture = new CompletableFuture<>();
    instance.addOnPrivateChatMessageListener(
        (username, chatMessage) -> {
          usernameFuture.complete(username);
          chatMessageFuture.complete(chatMessage);
        });

    String message = "private message";

    User user = mock(User.class);
    when(user.getNick()).thenReturn(chatUser1.getUsername());

    Channel channel = mock(Channel.class);
    when(channel.getName()).thenReturn(DEFAULT_CHANNEL_NAME);

    UserHostmask userHostMask = mock(UserHostmask.class);
    instance.onEvent(new PrivateMessageEvent(pircBotX, userHostMask, user, message));

    assertThat(chatMessageFuture.get().getMessage(), is(message));
    assertThat(chatMessageFuture.get().getUsername(), is(chatUser1.getUsername()));
    assertThat(
        chatMessageFuture.get().getTime(),
        is(greaterThan(Instant.ofEpochMilli(System.currentTimeMillis() - 1000))));
    assertThat(chatMessageFuture.get().isAction(), is(false));
  }
  @Test
  public void testAddOnChatUserQuitListener() throws Exception {
    CompletableFuture<Boolean> quitFuture = new CompletableFuture<>();
    instance.addOnChatUserQuitListener((username) -> quitFuture.complete(true));

    instance.onEvent(new QuitEvent(pircBotX, daoSnapshot, userHostMask, userSnapshot, "reason"));

    assertThat(quitFuture.get(TIMEOUT, TIMEOUT_UNIT), is(true));
  }
  @Test
  public void testAddOnChatUserJoinedChannelListener() throws Exception {
    when(chatPrefs.getChatColorMode()).thenReturn(chatColorMode.get());
    when(chatPrefs.getUserToColor()).thenReturn(userToColorProperty);

    CompletableFuture<String> channelNameFuture = new CompletableFuture<>();
    CompletableFuture<ChatUser> userFuture = new CompletableFuture<>();
    instance.addOnChatUserJoinedChannelListener(
        (channelName, chatUser) -> {
          channelNameFuture.complete(channelName);
          userFuture.complete(chatUser);
        });

    instance.onEvent(new JoinEvent(pircBotX, defaultChannel, userHostMask, user1));

    assertThat(channelNameFuture.get(TIMEOUT, TIMEOUT_UNIT), is(DEFAULT_CHANNEL_NAME));
    assertThat(userFuture.get(TIMEOUT, TIMEOUT_UNIT), is(chatUser1));
  }
  @Test
  public void testAddOnChatDisconnectedListener() throws Exception {
    CompletableFuture<Void> onChatDisconnectedFuture = new CompletableFuture<>();
    instance
        .connectionStateProperty()
        .addListener(
            (observable, oldValue, newValue) -> {
              switch (newValue) {
                case DISCONNECTED:
                  onChatDisconnectedFuture.complete(null);
                  break;
              }
            });

    instance.onEvent(new DisconnectEvent(pircBotX, daoSnapshot, null));

    onChatDisconnectedFuture.get(TIMEOUT, TIMEOUT_UNIT);
  }
  @Test
  public void testAddOnChatUserLeftChannelListener() throws Exception {
    CompletableFuture<String> usernameFuture = new CompletableFuture<>();
    CompletableFuture<String> channelNameFuture = new CompletableFuture<>();
    instance.addOnChatUserLeftChannelListener(
        (username, channelName) -> {
          usernameFuture.complete(username);
          channelNameFuture.complete(channelName);
        });

    when(channelSnapshot.getName()).thenReturn(DEFAULT_CHANNEL_NAME);
    when(userSnapshot.getNick()).thenReturn(chatUser1.getUsername());

    String reason = "Part reason";
    instance.onEvent(
        new PartEvent(pircBotX, daoSnapshot, channelSnapshot, userHostMask, userSnapshot, reason));

    assertThat(channelNameFuture.get(TIMEOUT, TIMEOUT_UNIT), is(DEFAULT_CHANNEL_NAME));
    assertThat(usernameFuture.get(TIMEOUT, TIMEOUT_UNIT), is(chatUser1.getUsername()));
  }
  @Test
  public void testAddOnUserListListener() throws Exception {
    CompletableFuture<String> channelNameFuture = new CompletableFuture<>();
    CompletableFuture<Map<String, ChatUser>> usersFuture = new CompletableFuture<>();
    instance.addOnUserListListener(
        (channelName, users) -> {
          channelNameFuture.complete(channelName);
          usersFuture.complete(users);
        });
    when(chatPrefs.getChatColorMode()).thenReturn(chatColorMode.get());
    when(chatPrefs.getUserToColor()).thenReturn(userToColorProperty);

    when(user2.compareTo(user1)).thenReturn(1);
    ImmutableSortedSet<User> users = ImmutableSortedSet.of(user1, user2);
    instance.onEvent(new UserListEvent(pircBotX, defaultChannel, users, true));

    assertThat(channelNameFuture.get(TIMEOUT, TIMEOUT_UNIT), is(DEFAULT_CHANNEL_NAME));

    Map<String, ChatUser> userMap = usersFuture.get(TIMEOUT, TIMEOUT_UNIT);
    assertThat(userMap.values(), hasSize(2));
    assertThat(userMap.get(chatUser1.getUsername()), is(chatUser1));
    assertThat(userMap.get(chatUser2.getUsername()), is(chatUser2));
  }
  @Test
  public void testAddOnChatConnectedListener() throws Exception {
    CompletableFuture<Boolean> onChatConnectedFuture = new CompletableFuture<>();

    instance
        .connectionStateProperty()
        .addListener(
            (observable, oldValue, newValue) -> {
              switch (newValue) {
                case CONNECTED:
                  onChatConnectedFuture.complete(null);
                  break;
              }
            });

    String password = "******";
    when(userService.getPassword()).thenReturn(password);

    mockTaskService();

    instance.onEvent(new ConnectEvent(pircBotX));

    assertThat(onChatConnectedFuture.get(TIMEOUT, TIMEOUT_UNIT), is(nullValue()));
  }