package com.wealthfront.magellan;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import com.wealthfront.magellan.transitions.NoAnimationTransition;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.fakes.RoboMenu;
import java.util.Deque;
import static com.google.common.truth.Truth.assertThat;
import static com.wealthfront.magellan.NavigationType.NO_ANIM;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.robolectric.Robolectric.setupActivity;
import static org.robolectric.RuntimeEnvironment.application;
@RunWith(RobolectricTestRunner.class)
public class NavigatorTest {
@Mock Screen root;
@Mock Screen screen;
@Mock Screen screen2;
@Mock Screen screen3;
@Mock ScreenLifecycleListener lifecycleListener;
NavigatorActivity activity;
Navigator navigator;
ScreenContainer container;
@Before
public void setUp() {
initMocks(this);
navigator = Navigator.withRoot(root)
.transition(new NoAnimationTransition())
.maxEventsTracked(2)
.build();
activity = spy(setupActivity(NavigatorActivity.class));
container = new ScreenContainer(application);
container.setId(R.id.magellan_container);
activity.setContentView(container);
when(root.createView(activity)).thenAnswer(
new Answer<View>() {
@Override
public View answer(InvocationOnMock invocation) throws Throwable {
return new BaseScreenView(application);
}
});
when(screen.createView(activity)).thenReturn(new BaseScreenView(application));
when(screen2.createView(activity)).thenReturn(new BaseScreenView(application));
when(screen3.createView(activity)).thenReturn(new BaseScreenView(application));
}
@Test
public void onCreateOnSaveOnDestroy() {
navigator.onCreate(activity, null);
verify(root).onRestore(null);
verify(root).createView(activity);
verify(root).onShow(activity);
Bundle state = new Bundle();
navigator.onSaveInstanceState(state);
verify(root).onSave(state);
navigator.onDestroy(activity);
verify(root).onHide(activity);
}
@Test
public void onCreateOptionsMenu() {
RoboMenu menu = new RoboMenu();
menu.add("item0");
menu.add("item1");
navigator.onCreateOptionsMenu(menu);
assertThat(menu.getItem(0).isVisible()).isFalse();
assertThat(menu.getItem(1).isVisible()).isFalse();
verify(root).onUpdateMenu(menu);
}
@Test
public void onPrepareOptionsMenu() {
RoboMenu menu = new RoboMenu();
menu.add("item0");
menu.add("item1");
navigator.onPrepareOptionsMenu(menu);
assertThat(menu.getItem(0).isVisible()).isFalse();
assertThat(menu.getItem(1).isVisible()).isFalse();
verify(root).onUpdateMenu(menu);
}
@Test
public void onResume() {
navigator.onCreate(activity, null);
reset(root);
navigator.onResume(activity);
verify(root).onResume(activity);
}
@Test
public void onPause() {
navigator.onCreate(activity, null);
navigator.onPause(activity);
verify(root).onPause(activity);
}
@Test
public void onResume_differentActivity() {
navigator.onCreate(activity, null);
reset(root);
verifyNoMoreInteractions(root);
navigator.onResume(new Activity());
}
@Test
public void onPause_differentActivity() {
navigator.onCreate(activity, null);
reset(root);
verifyNoMoreInteractions(root);
navigator.onPause(new Activity());
}
@Test
public void lifecycleListener() {
navigator.addLifecycleListener(lifecycleListener);
navigator.onCreate(activity, null);
navigator.goTo(screen);
navigator.goBack();
verify(lifecycleListener, times(2)).onShow(root);
verify(lifecycleListener).onShow(screen);
verify(lifecycleListener).onHide(root);
verify(lifecycleListener).onHide(screen);
navigator.removeLifecycleListener(lifecycleListener);
navigator.goTo(screen);
navigator.goBack();
verifyNoMoreInteractions(lifecycleListener);
}
@Test
public void handleBack() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
assertThat(navigator.currentScreen()).isEqualTo(screen);
boolean handled = navigator.handleBack();
assertThat(handled).isTrue();
assertThat(navigator.currentScreen()).isEqualTo(root);
handled = navigator.handleBack();
assertThat(handled).isFalse();
}
@Test
public void showHide() {
RoboMenu menu = new RoboMenu();
navigator.onCreate(activity, null);
navigator.onCreateOptionsMenu(menu);
navigator.show(screen);
verify(root).onHide(activity);
verify(activity, times(2)).onNavigate(isA(ActionBarConfig.class));
assertThat(navigator.currentScreen()).isEqualTo(screen);
reset(activity);
navigator.hide(screen);
assertThat(navigator.currentScreen()).isEqualTo(root);
verify(screen).onHide(activity);
verify(root, times(2)).onShow(activity);
verify(activity).onNavigate(isA(ActionBarConfig.class));
}
@Test
public void goBack() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
verify(root).onHide(activity);
verify(screen).onShow(activity);
assertThat(navigator.currentScreen()).isEqualTo(screen);
reset(activity);
navigator.goBack();
verify(screen).onHide(activity);
verify(root, times(2)).onShow(activity);
verify(activity).onNavigate(isA(ActionBarConfig.class));
assertThat(navigator.currentScreen()).isEqualTo(root);
}
@Test
public void goBackToRoot_notAtRoot() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
navigator.goTo(screen2);
navigator.goBackToRoot(NO_ANIM);
InOrder inOrder = inOrder(root);
inOrder.verify(root).onHide(activity);
inOrder.verify(root).onShow(activity);
assertThat(navigator.currentScreen()).isEqualTo(root);
}
@Test
public void goBackToRoot_alreadyAtRoot() {
navigator.onCreate(activity, null);
navigator.goBackToRoot(NO_ANIM);
InOrder inOrder = inOrder(root);
inOrder.verify(root).onHide(activity);
inOrder.verify(root).onShow(activity);
assertThat(navigator.currentScreen()).isEqualTo(root);
}
@Test(expected = IllegalArgumentException.class)
public void goBackTo_screenNotInHistory() {
navigator.onCreate(activity, null);
navigator.goBackTo(screen);
}
@Test
public void goBackTo() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
navigator.goTo(screen2);
navigator.goBackTo(screen);
assertThat(navigator.currentScreen()).isEqualTo(screen);
}
@Test(expected = IllegalArgumentException.class)
public void goBackBefore_screenNotInHistory() {
navigator.onCreate(activity, null);
navigator.goBackBefore(screen);
}
@Test
public void goBackBefore() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
navigator.goTo(screen2);
navigator.goBackBefore(screen);
assertThat(navigator.currentScreen()).isEqualTo(root);
}
@Test
public void saveRestore() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
Bundle state = new Bundle();
navigator.onSaveInstanceState(state);
verify(root).onSave(state);
verify(screen).onSave(state);
navigator.onDestroy(activity);
container.removeAllViews();
navigator.onCreate(activity, state);
verify(root).onRestore(state);
verify(screen).onRestore(state);
}
@Test
public void replace() {
navigator.onCreate(activity, null);
navigator.replace(screen);
verify(root).onHide(activity);
verify(screen).onShow(activity);
verify(activity, times(2)).onNavigate(isA(ActionBarConfig.class));
assertThat(navigator.currentScreen()).isEqualTo(screen);
boolean canGoBack = navigator.handleBack();
assertThat(canGoBack).isFalse();
}
@Test
public void resetWithRoot() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
assertThat(navigator.currentScreen()).isEqualTo(screen);
navigator.onDestroy(activity);
navigator.resetWithRoot(activity, root);
verify(screen).onHide(activity);
assertThat(navigator.currentScreen()).isEqualTo(root);
}
@Test
public void resetWithRoot_differentActivity() {
navigator.onCreate(activity, null);
navigator.resetWithRoot(new Activity(), root);
}
@Test(expected = IllegalStateException.class)
public void resetWithRoot_afterOnCreate() {
navigator.onCreate(activity, null);
navigator.resetWithRoot(activity, root);
}
@Test
public void rewriteHistory() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
assertThat(navigator.currentScreen()).isEqualTo(screen);
navigator.onDestroy(activity);
navigator.rewriteHistory(activity, new HistoryRewriter() {
@Override
public void rewriteHistory(Deque<Screen> history) {
history.pop();
}
});
assertThat(navigator.currentScreen()).isEqualTo(root);
}
@Test
public void rewriteHistory_differentActivity() {
navigator.onCreate(activity, null);
navigator.rewriteHistory(new Activity(), new HistoryRewriter() {
@Override
public void rewriteHistory(Deque<Screen> history) {
}
});
}
@Test(expected = IllegalStateException.class)
public void rewriteHistory_afterOnCreate() {
navigator.onCreate(activity, null);
navigator.rewriteHistory(activity, new HistoryRewriter() {
@Override
public void rewriteHistory(Deque<Screen> history) {
}
});
}
@Test
public void actionBarConfig() {
when(screen.shouldShowActionBar()).thenReturn(true);
when(screen.shouldAnimateActionBar()).thenReturn(true);
when(screen.getActionBarColorRes()).thenReturn(42);
navigator.onCreate(activity, null);
navigator.goTo(screen);
verify(screen).onShow(activity);
verify(activity, times(2)).onNavigate(isA(ActionBarConfig.class));
assertThat(activity.actionBarConfig.visible()).isTrue();
assertThat(activity.actionBarConfig.animated()).isTrue();
assertThat(activity.actionBarConfig.colorRes()).isEqualTo(42);
assertThat(navigator.currentScreen()).isEqualTo(screen);
}
@Test
public void navigate() {
when(screen.shouldShowActionBar()).thenReturn(false);
when(screen2.shouldShowActionBar()).thenReturn(true);
navigator.onCreate(activity, null);
navigator.goTo(screen);
navigator.navigate(new HistoryRewriter() {
@Override
public void rewriteHistory(Deque<Screen> history) {
history.clear();
history.push(root);
history.push(screen2);
}
});
verify(screen).onHide(activity);
verify(screen2).onShow(activity);
assertThat(navigator.currentScreen()).isEqualTo(screen2);
}
@Test
public void consumeTouchEventsDuringNavigate() {
Robolectric.getForegroundThreadScheduler().pause();
navigator.onCreate(activity, null);
assertThat(container.onInterceptTouchEvent(null)).isFalse();
navigator.goTo(screen);
assertThat(container.onInterceptTouchEvent(null)).isTrue();
}
@Test(expected = IllegalStateException.class)
public void goBackTooMuch() {
navigator.onCreate(activity, null);
navigator.goBack();
navigator.goBack();
}
@Test(expected = NullPointerException.class)
public void onCreateNotCalled() {
navigator.goTo(screen);
}
@Test(expected = IllegalStateException.class)
public void noContainer() {
activity.findViewById(R.id.magellan_container).setId(0);
navigator.onCreate(activity, null);
}
@Test
public void getEventsDescription() {
navigator.onCreate(activity, null);
navigator.goTo(screen);
assertThat(navigator.getEventsDescription()).isEqualTo(
"GO FORWARD - Backstack: root > [screen]\n"
);
navigator.goTo(screen2);
assertThat(navigator.getEventsDescription()).isEqualTo(
"GO FORWARD - Backstack: root > [screen]\n" +
"GO FORWARD - Backstack: root > screen > [screen2]\n"
);
navigator.goTo(screen3);
assertThat(navigator.getEventsDescription()).isEqualTo(
"GO FORWARD - Backstack: root > screen > [screen2]\n" +
"GO FORWARD - Backstack: root > screen > screen2 > [screen3]\n"
);
navigator.goBack();
assertThat(navigator.getEventsDescription()).isEqualTo(
"GO FORWARD - Backstack: root > screen > screen2 > [screen3]\n" +
"GO BACKWARD - Backstack: root > screen > [screen2]\n"
);
navigator.show(screen3);
assertThat(navigator.getEventsDescription()).isEqualTo(
"GO BACKWARD - Backstack: root > screen > [screen2]\n" +
"SHOW FORWARD - Backstack: root > screen > screen2 > [screen3]\n"
);
}
private static class NavigatorActivity extends Activity implements NavigationListener {
ActionBarConfig actionBarConfig;
@Override
public void onNavigate(ActionBarConfig actionBarConfig) {
this.actionBarConfig = actionBarConfig;
}
}
}