Skip to content

Commit

Permalink
redo ChatControllerTest
Browse files Browse the repository at this point in the history
  • Loading branch information
Marc-Spector committed Jun 18, 2024
1 parent cef190d commit e418624
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 79 deletions.
208 changes: 129 additions & 79 deletions src/test/java/com/faforever/client/chat/ChatControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,145 +1,195 @@
package com.faforever.client.chat;

import com.faforever.client.net.ConnectionState;
import com.faforever.client.notification.NotificationService;
import com.faforever.client.test.PlatformTest;
import com.faforever.client.theme.UiService;
import com.faforever.client.user.LoginService;
import com.faforever.commons.api.dto.MeResult;
import javafx.beans.InvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.scene.control.Tab;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.Set;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static javafx.collections.FXCollections.observableHashMap;
import static javafx.collections.FXCollections.synchronizedObservableMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

// TODO those unit tests need to be improved (missing verifications)
public class ChatControllerTest extends PlatformTest {

public static final String TEST_USER_NAME = "junit";
private static final String TEST_CHANNEL_NAME = "#testChannel";

@Mock
private ChannelTabController channelTabController;
@Mock
private PrivateChatTabController privateChatTabController;
@Mock
private LoginService loginService;
private AbstractChatTabController tabController;
@Mock
private UiService uiService;
@Mock
private ChatService chatService;
@Mock
private NotificationService notificationService;
@Mock
@Spy
private ChatNavigation chatNavigation;
@Captor
private ArgumentCaptor<MapChangeListener<String, ChatChannel>> channelsListener;
private final ObservableMap<String, ChatChannel> channels = synchronizedObservableMap(observableHashMap());

private final ObjectProperty<ConnectionState> connectionState = new SimpleObjectProperty<>(ConnectionState.DISCONNECTED);
@InjectMocks
private ChatController instance;
private SimpleObjectProperty<ConnectionState> connectionState;
private ObservableList<Tab> openedTabs;

@BeforeEach
public void setUp() throws Exception {
connectionState = new SimpleObjectProperty<>(ConnectionState.DISCONNECTED);

lenient().when(uiService.loadFxml("theme/chat/private_chat_tab.fxml")).thenReturn(privateChatTabController);
lenient().when(uiService.loadFxml("theme/chat/channel_tab.fxml")).thenReturn(channelTabController);
lenient().when(loginService.getUsername()).thenReturn(TEST_USER_NAME);
lenient().when(loginService.getOwnUser()).thenReturn(new MeResult());
lenient().when(chatService.connectionStateProperty()).thenReturn(connectionState);
lenient().when(uiService.loadFxml("theme/chat/private_chat_tab.fxml")).thenReturn(tabController);
lenient().when(uiService.loadFxml("theme/chat/channel_tab.fxml")).thenReturn(tabController);
lenient().when(tabController.getRoot()).thenAnswer(invocation -> new Tab());


loadFxml("theme/chat/chat.fxml", clazz -> instance);
openedTabs = instance.tabPane.getTabs();
}

private void setChannelsListener() {
verify(chatService).addChannelsListener(channelsListener.capture());
channels.addListener(channelsListener.getValue());
}

@Test
public void testOnDisconnected() throws Exception {
connectionState.set(ConnectionState.DISCONNECTED);
public void testOnlyOneAddChannelTabAfterInitialized() {
assertEquals(1, openedTabs.size());
assertEquals(ChatController.ADD_CHANNEL_TAB_ID, openedTabs.getFirst().getId());
}

@Test
public void testGetRoot() throws Exception {
assertThat(instance.getRoot(), is(instance.chatRoot));
assertThat(instance.getRoot().getParent(), is(nullValue()));
public void testOnChannelJoined() {
setChannelsListener();

requestChannel("aeolus", true);
requestChannel("channel");
requestChannel("player");

assertContainsTab("aeolus");
assertContainsTab("channel");
assertContainsTab("player");
}

@Test
public void testAddedTabShouldBeSelected() {
setChannelsListener();
requestChannel("channel");

assertEquals("channel", chatNavigation.getLastOpenedTabId());
assertEquals("channel", instance.tabPane.getSelectionModel().getSelectedItem().getId());
}

@Test
public void testOnChannelsJoinedRequest() throws Exception {
channelJoined(TEST_CHANNEL_NAME);
public void testSelectedTabShouldBeRemembered() {
setChannelsListener();
requestChannel("channel1");
requestChannel("channel2");

assertEquals("channel2", chatNavigation.getLastOpenedTabId());
assertEquals("channel2", instance.tabPane.getSelectionModel().getSelectedItem().getId());

runOnFxThreadAndWait(() -> instance.tabPane.getSelectionModel().selectPrevious());

assertEquals("channel1", chatNavigation.getLastOpenedTabId());
assertEquals("channel1", instance.tabPane.getSelectionModel().getSelectedItem().getId());
}

connectionState.set(ConnectionState.DISCONNECTED);
private void assertContainsTab(String channel) {
assertTrue(openedTabs.stream().anyMatch(tab -> tab.getId().equals(channel)));
}

private void channelJoined(String channel) {
MapChangeListener.Change<? extends String, ? extends ChatChannel> testChannelChange = mock(MapChangeListener.Change.class);
channelsListener.getValue().onChanged(testChannelChange);
@Test
public void testOnDisconnected() {
setChannelsListener();

connectionState.set(ConnectionState.CONNECTED);
requestChannel("aeolus", true);

runOnFxThreadAndWait(() -> connectionState.set(ConnectionState.DISCONNECTED));
assertTrue(instance.disconnectedPane.isVisible());
assertFalse(instance.connectingProgressPane.isVisible());
assertFalse(instance.tabPane.isVisible());
assertEquals(1, openedTabs.size());
}

@Test
@Disabled("Flaky test")
public void testOnJoinChannelButtonClicked() throws Exception {
assertEquals(instance.tabPane.getTabs().size(), 1);

Tab tab = new Tab();
tab.setId(TEST_CHANNEL_NAME);

when(channelTabController.getRoot()).thenReturn(tab);
when(loginService.getUsername()).thenReturn(TEST_USER_NAME);
doAnswer(invocation -> {
MapChangeListener.Change<? extends String, ? extends ChatChannel> change = mock(MapChangeListener.Change.class);
when(change.wasAdded()).thenReturn(true);
doReturn(new ChatChannel(invocation.getArgument(0))).when(change).getValueAdded();
channelsListener.getValue().onChanged(change);
return null;
}).when(chatService).joinChannel(anyString());

instance.channelNameTextField.setText(TEST_CHANNEL_NAME);
instance.onJoinChannelButtonClicked();

verify(chatService).joinChannel(TEST_CHANNEL_NAME);

CountDownLatch tabAddedLatch = new CountDownLatch(1);
instance.tabPane.getTabs().addListener((InvalidationListener) observable -> tabAddedLatch.countDown());
tabAddedLatch.await(2, TimeUnit.SECONDS);

assertThat(instance.tabPane.getTabs(), hasSize(2));
assertThat(instance.tabPane.getTabs().getFirst().getId(), is(TEST_CHANNEL_NAME));
public void testOnConnected() {
assertEquals(ConnectionState.DISCONNECTED, connectionState.getValue());

ChatChannel chatChannelMock1 = mock(ChatChannel.class);
ChatChannel chatChannelMock2 = mock(ChatChannel.class);
when(chatChannelMock1.getName()).thenReturn("1");
when(chatChannelMock2.getName()).thenReturn("2");
when(chatService.getChannels()).thenReturn(Set.of(chatChannelMock1, chatChannelMock2));

runOnFxThreadAndWait(() -> connectionState.set(ConnectionState.CONNECTED));

assertFalse(instance.disconnectedPane.isVisible());
assertFalse(instance.connectingProgressPane.isVisible());
assertTrue(instance.tabPane.isVisible());
assertEquals(3, openedTabs.size());
}

@Test
public void testOnJoinChannelButtonClickedInvalidChannel() throws Exception {
assertEquals(instance.tabPane.getTabs().size(), 1);
public void testOnConnecting() {
assertEquals(ConnectionState.DISCONNECTED, connectionState.getValue());
runOnFxThreadAndWait(() -> connectionState.set(ConnectionState.CONNECTING));
assertFalse(instance.disconnectedPane.isVisible());
assertTrue(instance.connectingProgressPane.isVisible());
assertFalse(instance.tabPane.isVisible());
}

Tab tab = new Tab();
tab.setId(TEST_CHANNEL_NAME);
private void requestChannel(String channel) {
requestChannel(channel, false);
}

instance.channelNameTextField.setText(TEST_CHANNEL_NAME.replace("#", ""));
instance.onJoinChannelButtonClicked();
private void requestChannel(String channel, boolean isDefaultChannel) {
ChatChannel chatChannelMock = mock(ChatChannel.class);
when(chatChannelMock.getName()).thenReturn(channel);
when(chatService.isDefaultChannel(chatChannelMock)).thenReturn(isDefaultChannel);
channels.putIfAbsent(channel, chatChannelMock);
waitFxEvents();
}

@Test
public void testOnChannelLeft() {
setChannelsListener();
requestChannel("aeolus", true);
requestChannel("channel", false);

Tab tab = openedTabs.stream().filter(tab1 -> tab1.getId().equals("aeolus")).findFirst().orElseThrow();
runOnFxThreadAndWait(() -> openedTabs.remove(tab));
assertEquals(2, openedTabs.size());
}

verify(chatService).joinChannel(TEST_CHANNEL_NAME);
@Test
public void testOnJoinChannelButtonClicked() {
runOnFxThreadAndWait(() -> {
instance.channelNameTextField.setText("newChannel");
instance.onJoinChannelButtonClicked();
});

verify(chatService).joinChannel("#newChannel");
assertTrue(instance.channelNameTextField.getText().isEmpty());
}

@Test
public void testOnConnectButtonClicked() {
runOnFxThreadAndWait(() -> instance.onConnectButtonClicked());
verify(chatService).connect();
}
}
4 changes: 4 additions & 0 deletions src/test/java/com/faforever/client/test/PlatformTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ protected URL getThemeFileUrl(String file) throws IOException {

protected void runOnFxThreadAndWait(Runnable runnable) {
Platform.runLater(runnable);
waitFxEvents();
}

protected void waitFxEvents() {
WaitForAsyncUtils.waitForFxEvents();
}

Expand Down

0 comments on commit e418624

Please sign in to comment.