From 5470e9d812d6fe61e7af405d8eb7908638b1aa60 Mon Sep 17 00:00:00 2001 From: Ward Abbass Date: Wed, 24 Nov 2021 09:55:22 +0200 Subject: [PATCH 01/14] Bottom tabs test to kt --- .../bottomtabs/BottomTabsControllerTest.java | 511 ------------------ .../bottomtabs/BottomTabsControllerTest.kt | 467 ++++++++++++++++ 2 files changed, 467 insertions(+), 511 deletions(-) delete mode 100644 lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.java create mode 100644 lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.java deleted file mode 100644 index bb1ef6cbe57..00000000000 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.java +++ /dev/null @@ -1,511 +0,0 @@ -package com.reactnativenavigation.viewcontrollers.bottomtabs; - -import android.app.Activity; -import android.graphics.Color; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup.MarginLayoutParams; - -import com.aurelhubert.ahbottomnavigation.AHBottomNavigation; -import com.reactnativenavigation.BaseTest; -import com.reactnativenavigation.TestUtils; -import com.reactnativenavigation.mocks.ImageLoaderMock; -import com.reactnativenavigation.mocks.SimpleViewController; -import com.reactnativenavigation.mocks.TypefaceLoaderMock; -import com.reactnativenavigation.options.BottomTabsOptions; -import com.reactnativenavigation.options.Options; -import com.reactnativenavigation.options.params.Bool; -import com.reactnativenavigation.options.params.Colour; -import com.reactnativenavigation.options.params.NullText; -import com.reactnativenavigation.options.params.Number; -import com.reactnativenavigation.options.params.ThemeColour; -import com.reactnativenavigation.options.params.Text; -import com.reactnativenavigation.react.CommandListenerAdapter; -import com.reactnativenavigation.react.events.EventEmitter; -import com.reactnativenavigation.utils.ImageLoader; -import com.reactnativenavigation.utils.OptionHelper; -import com.reactnativenavigation.utils.StatusBarUtils; -import com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.BottomTabsAttacher; -import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry; -import com.reactnativenavigation.viewcontrollers.fakes.FakeParentController; -import com.reactnativenavigation.viewcontrollers.parent.ParentController; -import com.reactnativenavigation.viewcontrollers.stack.StackController; -import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter; -import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController; -import com.reactnativenavigation.views.bottomtabs.BottomTabs; -import com.reactnativenavigation.views.bottomtabs.BottomTabsContainer; -import com.reactnativenavigation.views.bottomtabs.BottomTabsLayout; - -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.coordinatorlayout.widget.CoordinatorLayout; - - -import static com.reactnativenavigation.TestUtils.hideBackButton; -import static com.reactnativenavigation.utils.ObjectUtils.perform; -import static org.assertj.core.api.Java6Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class BottomTabsControllerTest extends BaseTest { - - private Activity activity; - private BottomTabs bottomTabs; - private BottomTabsContainer bottomTabsContainer; - private BottomTabsController uut; - private final Options initialOptions = new Options(); - private ViewController child1; - private ViewController child2; - private ViewController child3; - private ViewController stackChild; - private StackController child4; - private ViewController child5; - private final Options tabOptions = OptionHelper.createBottomTabOptions(); - private final ImageLoader imageLoaderMock = ImageLoaderMock.mock(); - private EventEmitter eventEmitter; - private ChildControllersRegistry childRegistry; - private List> tabs; - private BottomTabsPresenter presenter; - private BottomTabPresenter bottomTabPresenter; - private BottomTabsAttacher tabsAttacher; - - @Override - public void beforeEach() { - activity = newActivity(); - childRegistry = new ChildControllersRegistry(); - eventEmitter = Mockito.mock(EventEmitter.class); - prepareViewsForTests(); - StatusBarUtils.saveStatusBarHeight(63); - } - - @Test - public void createView_checkProperStructure() { - idleMainLooper(); - assertThat(uut.getView()).isInstanceOf(CoordinatorLayout.class); - assertThat(uut.getView().getChildAt(uut.getView().getChildCount() - 1)).isInstanceOf(BottomTabsContainer.class); - assertThat(((CoordinatorLayout.LayoutParams) uut.getBottomTabsContainer().getLayoutParams()).gravity).isEqualTo(Gravity.BOTTOM); - } - - @Test - public void createView_tabsWithoutIconsAreAccepted() { - tabOptions.bottomTabOptions.icon = new NullText(); - prepareViewsForTests(); - assertThat(uut.getBottomTabs().getItemsCount()).isEqualTo(tabs.size()); - } - - @Test - public void createView_showTitlesWhenAllTabsDontHaveIcons() { - tabOptions.bottomTabOptions.icon = new NullText(); - assertThat(tabOptions.bottomTabsOptions.titleDisplayMode.hasValue()).isFalse(); - prepareViewsForTests(); - presenter.applyOptions(Options.EMPTY); - assertThat(bottomTabsContainer.getBottomTabs().getTitleState()).isEqualTo(AHBottomNavigation.TitleState.ALWAYS_SHOW); - } - - @Test(expected = RuntimeException.class) - public void setTabs_ThrowWhenMoreThan5() { - tabs.add(new SimpleViewController(activity, childRegistry, "6", tabOptions)); - createBottomTabs(); - } - - @Test - public void parentControllerIsSet() { - uut = createBottomTabs(); - for (ViewController tab : tabs) { - assertThat(tab.getParentController()).isEqualTo(uut); - } - } - - @Test - public void setTabs_allChildViewsAreAttachedToHierarchy() { - uut.onViewWillAppear(); - assertThat(uut.getView().getChildCount()).isEqualTo(6); - for (ViewController child : uut.getChildControllers()) { - assertThat(child.getView().getParent()).isNotNull(); - } - } - - @Test - public void setTabs_firstChildIsVisibleOtherAreGone() { - uut.onViewWillAppear(); - for (int i = 0; i < uut.getChildControllers().size(); i++) { - assertThat(uut.getView().getChildAt(i)).isEqualTo(tabs.get(i).getView()); - assertThat(uut.getView().getChildAt(i).getVisibility()).isEqualTo(i == 0 ? View.VISIBLE : View.INVISIBLE); - } - } - - @Test - public void onTabSelected() { - uut.ensureViewIsCreated(); - assertThat(uut.getSelectedIndex()).isZero(); - assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getVisibility()).isEqualTo(View.VISIBLE); - - uut.onTabSelected(3, false); - - assertThat(uut.getSelectedIndex()).isEqualTo(3); - assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getVisibility()).isEqualTo(View.INVISIBLE); - assertThat(((ViewController) ((List) uut.getChildControllers()).get(3)).getView().getVisibility()).isEqualTo(View.VISIBLE); - verify(eventEmitter).emitBottomTabSelected(0, 3); - } - - @Test - public void onTabReSelected() { - uut.ensureViewIsCreated(); - assertThat(uut.getSelectedIndex()).isZero(); - - uut.onTabSelected(0, true); - - assertThat(uut.getSelectedIndex()).isEqualTo(0); - assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getParent()).isNotNull(); - verify(eventEmitter).emitBottomTabSelected(0, 0); - } - - @Test - public void handleBack_DelegatesToSelectedChild() { - uut.ensureViewIsCreated(); - assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse(); - uut.selectTab(4); - assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue(); - verify(child5).handleBack(any()); - } - - @Test - public void applyChildOptions_bottomTabsOptionsAreClearedAfterApply() { - ParentController parent = Mockito.mock(ParentController.class); - uut.setParentController(parent); - - child1.options.bottomTabsOptions.backgroundColor = new ThemeColour(new Colour(Color.RED)); - child1.onViewWillAppear(); - - ArgumentCaptor optionsCaptor = ArgumentCaptor.forClass(Options.class); - verify(parent).applyChildOptions(optionsCaptor.capture(), any()); - assertThat(optionsCaptor.getValue().bottomTabsOptions.backgroundColor.hasValue()).isFalse(); - } - - @Test - public void applyOptions_bottomTabsCreateViewOnlyOnce() { - idleMainLooper(); - verify(presenter).applyOptions(any()); - verify(bottomTabsContainer.getBottomTabs(), times(2)).superCreateItems(); // first time when view is created, second time when options are applied - } - - @Test - public void onSizeChanged_recreateItemsIfSizeHasChanged() { - int numberOfPreviousInvocations = 1; - bottomTabs.onSizeChanged(0, 0, 0, 0); - verify(bottomTabs, times(numberOfPreviousInvocations)).superCreateItems(); - - bottomTabs.onSizeChanged(100, 0, 0, 0); - verify(bottomTabs, times(numberOfPreviousInvocations)).superCreateItems(); - - bottomTabs.onSizeChanged(1080, 147, 0, 0); - verify(bottomTabs, times(numberOfPreviousInvocations + 1)).superCreateItems(); - - bottomTabs.onSizeChanged(1920, 147, 0, 0); - verify(bottomTabs, times(numberOfPreviousInvocations + 2)).superCreateItems(); - - when(bottomTabs.getItemsCount()).thenReturn(0); - bottomTabs.onSizeChanged(1080, 147, 0, 0); - verify(bottomTabs, times(numberOfPreviousInvocations + 2)).superCreateItems(); - } - - @Test - public void mergeOptions_currentTabIndex() { - uut.ensureViewIsCreated(); - assertThat(uut.getSelectedIndex()).isZero(); - - Options options = new Options(); - options.bottomTabsOptions.currentTabIndex = new Number(1); - uut.mergeOptions(options); - assertThat(uut.getSelectedIndex()).isOne(); - verify(eventEmitter, times(0)).emitBottomTabSelected(any(Integer.class), any(Integer.class)); - } - - @Test - public void mergeOptions_drawBehind() { - assertThat(uut.getBottomInset(child1)).isEqualTo(uut.getBottomTabs().getHeight()); - - Options o1 = new Options(); - o1.bottomTabsOptions.drawBehind = new Bool(true); - child1.mergeOptions(o1); - assertThat(uut.getBottomInset(child1)).isEqualTo(0); - - Options o2 = new Options(); - o2.topBar.title.text = new Text("Some text"); - child1.mergeOptions(o1); - assertThat(uut.getBottomInset(child1)).isEqualTo(0); - } - - @Test - public void mergeOptions_drawBehind_stack() { - uut.ensureViewIsCreated(); - uut.selectTab(3); - - assertThat(((MarginLayoutParams) stackChild.getView().getLayoutParams()).bottomMargin).isEqualTo(bottomTabs.getHeight()); - - Options o1 = new Options(); - o1.bottomTabsOptions.drawBehind = new Bool(true); - stackChild.mergeOptions(o1); - - assertThat(((MarginLayoutParams) stackChild.getView().getLayoutParams()).bottomMargin).isEqualTo(0); - } - - @Test - public void mergeOptions_mergesBottomTabOptions() { - Options options = new Options(); - uut.mergeOptions(options); - verify(bottomTabPresenter).mergeOptions(options); - } - - @Test - public void applyChildOptions_resolvedOptionsAreUsed() { - Options childOptions = new Options(); - SimpleViewController pushedScreen = new SimpleViewController(activity, childRegistry, "child4.1", childOptions); - disablePushAnimation(pushedScreen); - child4 = spyOnStack(pushedScreen); - - tabs = new ArrayList<>(Collections.singletonList(child4)); - tabsAttacher = new BottomTabsAttacher(tabs, presenter, Options.EMPTY); - - initialOptions.bottomTabsOptions.currentTabIndex = new Number(0); - Options resolvedOptions = new Options(); - uut = new BottomTabsController(activity, - tabs, - childRegistry, - eventEmitter, - imageLoaderMock, - "uut", - initialOptions, - new Presenter(activity, new Options()), - tabsAttacher, - presenter, - new BottomTabPresenter(activity, tabs, ImageLoaderMock.mock(), new TypefaceLoaderMock(), new Options())) { - @Override - public Options resolveCurrentOptions() { - return resolvedOptions; - } - - @NonNull - @Override - protected BottomTabs createBottomTabs() { - return new BottomTabs(activity) { - @Override - protected void createItems() { - - } - }; - } - }; - - activity.setContentView(uut.getView()); - idleMainLooper(); - verify(presenter, times(2)).applyChildOptions(eq(resolvedOptions), any()); - } - - @Test - public void child_mergeOptions_currentTabIndex() { - uut.ensureViewIsCreated(); - - assertThat(uut.getSelectedIndex()).isZero(); - - Options options = new Options(); - options.bottomTabsOptions.currentTabIndex = new Number(1); - child1.mergeOptions(options); - - assertThat(uut.getSelectedIndex()).isOne(); - } - - @Test - public void resolveCurrentOptions_returnsFirstTabIfInvokedBeforeViewIsCreated() { - uut = createBottomTabs(); - assertThat(uut.getCurrentChild()).isEqualTo(tabs.get(0)); - } - - @Test - public void buttonPressInvokedOnCurrentTab() { - uut.ensureViewIsCreated(); - uut.selectTab(4); - - uut.sendOnNavigationButtonPressed("btn1"); - verify(child5, times(1)).sendOnNavigationButtonPressed("btn1"); - } - - @Test - public void push() { - uut.selectTab(3); - - SimpleViewController stackChild2 = new SimpleViewController(activity, childRegistry, "stackChild2", new Options()); - disablePushAnimation(stackChild2); - hideBackButton(stackChild2); - - assertThat(child4.size()).isEqualTo(1); - child4.push(stackChild2, new CommandListenerAdapter()); - assertThat(child4.size()).isEqualTo(2); - } - - @Test - public void oneTimeOptionsAreAppliedOnce() { - Options options = new Options(); - options.bottomTabsOptions.currentTabIndex = new Number(1); - - assertThat(uut.getSelectedIndex()).isZero(); - uut.mergeOptions(options); - assertThat(uut.getSelectedIndex()).isOne(); - assertThat(uut.options.bottomTabsOptions.currentTabIndex.hasValue()).isFalse(); - assertThat(uut.initialOptions.bottomTabsOptions.currentTabIndex.hasValue()).isFalse(); - } - - @Test - public void selectTab() { - uut.selectTab(1); - verify(tabsAttacher).onTabSelected(tabs.get(1)); - } - - @Test - public void selectTab_onViewDidAppearIsInvokedAfterSelection() { - uut.selectTab(1); - verify(child2).onViewDidAppear(); - } - - @Test - public void creatingTabs_onViewDidAppearInvokedAfterInitialTabIndexSet() { - Options options = Options.EMPTY.copy(); - options.bottomTabsOptions.currentTabIndex = new Number(1); - prepareViewsForTests(options.bottomTabsOptions); - idleMainLooper(); - verify(tabs.get(0), times(0)).onViewDidAppear(); - verify(tabs.get(1), times(1)).onViewDidAppear(); - verify(tabs.get(2), times(0)).onViewDidAppear(); - verify(tabs.get(3), times(0)).onViewDidAppear(); - verify(tabs.get(4), times(0)).onViewDidAppear(); - } - - @Test - public void getTopInset() { - assertThat(child1.getTopInset()).isEqualTo(getStatusBarHeight()); - assertThat(child2.getTopInset()).isEqualTo(getStatusBarHeight()); - - child1.options.statusBar.drawBehind = new Bool(true); - assertThat(child1.getTopInset()).isEqualTo(0); - assertThat(child2.getTopInset()).isEqualTo(getStatusBarHeight()); - - assertThat(stackChild.getTopInset()).isEqualTo(getStatusBarHeight() + child4.getTopBar().getHeight()); - } - - @Test - public void getBottomInset_defaultOptionsAreTakenIntoAccount() { - Options defaultOptions = new Options(); - defaultOptions.bottomTabsOptions.visible = new Bool(false); - - assertThat(uut.getBottomInset(child1)).isEqualTo(bottomTabs.getHeight()); - uut.setDefaultOptions(defaultOptions); - assertThat(uut.getBottomInset(child1)).isZero(); - } - - @Test - public void destroy() { - uut.destroy(); - verify(tabsAttacher).destroy(); - } - - private void prepareViewsForTests() { - prepareViewsForTests(initialOptions.bottomTabsOptions); - } - - private void prepareViewsForTests(BottomTabsOptions bottomTabsOptions) { - perform(uut, ViewController::destroy); - bottomTabs = spy(new BottomTabs(activity) { - @Override - public void superCreateItems() { - - } - }); - bottomTabsContainer = spy(new BottomTabsContainer(activity, bottomTabs)); - - createChildren(); - tabs = Arrays.asList(child1, child2, child3, child4, child5); - initialOptions.bottomTabsOptions = bottomTabsOptions; - presenter = spy(new BottomTabsPresenter(tabs, initialOptions, new BottomTabsAnimator())); - bottomTabPresenter = spy(new BottomTabPresenter(activity, tabs, ImageLoaderMock.mock(), new TypefaceLoaderMock(), initialOptions)); - tabsAttacher = spy(new BottomTabsAttacher(tabs, presenter, initialOptions)); - uut = createBottomTabs(); - activity.setContentView(new FakeParentController(activity, childRegistry, uut).getView()); - } - - private void createChildren() { - child1 = spy(new SimpleViewController(activity, childRegistry, "child1", tabOptions)); - child2 = spy(new SimpleViewController(activity, childRegistry, "child2", tabOptions)); - child3 = spy(new SimpleViewController(activity, childRegistry, "child3", tabOptions)); - stackChild = spy(new SimpleViewController(activity, childRegistry, "stackChild", tabOptions)); - child4 = spyOnStack(stackChild); - child5 = spy(new SimpleViewController(activity, childRegistry, "child5", tabOptions)); - when(child5.handleBack(any())).thenReturn(true); - } - - private StackController spyOnStack(ViewController initialChild) { - StackController build = TestUtils.newStackController(activity) - .setInitialOptions(tabOptions) - .build(); - StackController stack = spy(build); - disablePushAnimation(initialChild); - stack.ensureViewIsCreated(); - stack.push(initialChild, new CommandListenerAdapter()); - return stack; - } - - private BottomTabsController createBottomTabs() { - return new BottomTabsController(activity, - tabs, - childRegistry, - eventEmitter, - imageLoaderMock, - "uut", - initialOptions, - new Presenter(activity, initialOptions), - tabsAttacher, - presenter, - bottomTabPresenter) { - @Override - public void ensureViewIsCreated() { - super.ensureViewIsCreated(); - uut.getView().layout(0, 0, 1000, 1000); - } - - @NonNull - @Override - public BottomTabsLayout createView() { - BottomTabsLayout view = super.createView(); - bottomTabs.getLayoutParams().height = 100; - return view; - } - - @NonNull - @Override - protected BottomTabsContainer createBottomTabsContainer() { - return bottomTabsContainer; - } - - @NonNull - @Override - protected BottomTabs createBottomTabs() { - return bottomTabs; - } - }; - } - - private int getStatusBarHeight() { - return StatusBarUtils.getStatusBarHeight(activity); - } -} diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt new file mode 100644 index 00000000000..75727a34c34 --- /dev/null +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt @@ -0,0 +1,467 @@ +package com.reactnativenavigation.viewcontrollers.bottomtabs + +import android.app.Activity +import android.graphics.Color +import android.view.Gravity +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState +import com.reactnativenavigation.BaseTest +import com.reactnativenavigation.TestUtils +import com.reactnativenavigation.mocks.ImageLoaderMock.mock +import com.reactnativenavigation.mocks.SimpleViewController +import com.reactnativenavigation.mocks.TypefaceLoaderMock +import com.reactnativenavigation.options.BottomTabsOptions +import com.reactnativenavigation.options.Options +import com.reactnativenavigation.options.params.* +import com.reactnativenavigation.options.params.Number +import com.reactnativenavigation.react.CommandListenerAdapter +import com.reactnativenavigation.react.events.EventEmitter +import com.reactnativenavigation.utils.ObjectUtils +import com.reactnativenavigation.utils.OptionHelper +import com.reactnativenavigation.utils.StatusBarUtils.getStatusBarHeight +import com.reactnativenavigation.utils.StatusBarUtils.saveStatusBarHeight +import com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.BottomTabsAttacher +import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry +import com.reactnativenavigation.viewcontrollers.fakes.FakeParentController +import com.reactnativenavigation.viewcontrollers.parent.ParentController +import com.reactnativenavigation.viewcontrollers.stack.StackController +import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter +import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController +import com.reactnativenavigation.views.bottomtabs.BottomTabs +import com.reactnativenavigation.views.bottomtabs.BottomTabsContainer +import com.reactnativenavigation.views.bottomtabs.BottomTabsLayout +import org.assertj.core.api.Java6Assertions +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mockito +import java.util.* + +class BottomTabsControllerTest : BaseTest() { + private lateinit var activity: Activity + private lateinit var bottomTabs: BottomTabs + private lateinit var bottomTabsContainer: BottomTabsContainer + private lateinit var uut: BottomTabsController + private val initialOptions = Options() + private lateinit var child1: ViewController<*> + private lateinit var child2: ViewController<*> + private lateinit var child3: ViewController<*> + private lateinit var stackChild: ViewController<*> + private lateinit var child4: StackController + private lateinit var child5: ViewController<*> + private val tabOptions = OptionHelper.createBottomTabOptions() + private val imageLoaderMock = mock() + private lateinit var eventEmitter: EventEmitter + private lateinit var childRegistry: ChildControllersRegistry + private lateinit var tabs: MutableList> + private lateinit var presenter: BottomTabsPresenter + private lateinit var bottomTabPresenter: BottomTabPresenter + private lateinit var tabsAttacher: BottomTabsAttacher + override fun beforeEach() { + activity = newActivity() + childRegistry = ChildControllersRegistry() + eventEmitter = Mockito.mock(EventEmitter::class.java) + prepareViewsForTests() + saveStatusBarHeight(63) + } + + @Test + fun createView_checkProperStructure() { + idleMainLooper() + Java6Assertions.assertThat(uut.view).isInstanceOf(CoordinatorLayout::class.java) + Java6Assertions.assertThat(uut.view.getChildAt(uut.view.childCount - 1)).isInstanceOf( + BottomTabsContainer::class.java + ) + Java6Assertions.assertThat((uut.bottomTabsContainer.layoutParams as CoordinatorLayout.LayoutParams).gravity) + .isEqualTo(Gravity.BOTTOM) + } + + @Test + fun createView_tabsWithoutIconsAreAccepted() { + tabOptions.bottomTabOptions.icon = NullText() + prepareViewsForTests() + Java6Assertions.assertThat(uut.bottomTabs.itemsCount).isEqualTo(tabs.size) + } + + @Test + fun createView_showTitlesWhenAllTabsDontHaveIcons() { + tabOptions.bottomTabOptions.icon = NullText() + Java6Assertions.assertThat(tabOptions.bottomTabsOptions.titleDisplayMode.hasValue()).isFalse + prepareViewsForTests() + presenter.applyOptions(Options.EMPTY) + Java6Assertions.assertThat(bottomTabsContainer.bottomTabs.titleState).isEqualTo(TitleState.ALWAYS_SHOW) + } + + @Test(expected = RuntimeException::class) + fun setTabs_ThrowWhenMoreThan5() { + tabs.add(SimpleViewController(activity, childRegistry, "6", tabOptions)) + createBottomTabs() + } + + @Test + fun parentControllerIsSet() { + uut = createBottomTabs() + for (tab in tabs) { + Java6Assertions.assertThat(tab.parentController).isEqualTo(uut) + } + } + + @Test + fun setTabs_allChildViewsAreAttachedToHierarchy() { + uut.onViewWillAppear() + Java6Assertions.assertThat(uut.view.childCount).isEqualTo(6) + for (child in uut.childControllers) { + Java6Assertions.assertThat(child.view.parent).isNotNull + } + } + + @Test + fun setTabs_firstChildIsVisibleOtherAreGone() { + uut.onViewWillAppear() + for (i in uut.childControllers.indices) { + Java6Assertions.assertThat(uut.view.getChildAt(i)).isEqualTo(tabs[i].view) + Java6Assertions.assertThat(uut.view.getChildAt(i).visibility) + .isEqualTo(if (i == 0) View.VISIBLE else View.INVISIBLE) + } + } + + @Test + fun onTabSelected() { + uut.ensureViewIsCreated() + Java6Assertions.assertThat(uut.selectedIndex).isZero + Java6Assertions.assertThat(((uut.childControllers as List<*>)[0] as ViewController<*>).view.visibility) + .isEqualTo( + View.VISIBLE + ) + uut.onTabSelected(3, false) + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(3) + Java6Assertions.assertThat(((uut.childControllers as List<*>)[0] as ViewController<*>).view.visibility) + .isEqualTo( + View.INVISIBLE + ) + Java6Assertions.assertThat(((uut.childControllers as List<*>)[3] as ViewController<*>).view.visibility) + .isEqualTo( + View.VISIBLE + ) + Mockito.verify(eventEmitter).emitBottomTabSelected(0, 3) + } + + @Test + fun onTabReSelected() { + uut.ensureViewIsCreated() + Java6Assertions.assertThat(uut.selectedIndex).isZero + uut.onTabSelected(0, true) + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(0) + Java6Assertions.assertThat(((uut.childControllers as List<*>)[0] as ViewController<*>).view.parent).isNotNull + Mockito.verify(eventEmitter).emitBottomTabSelected(0, 0) + } + + @Test + fun handleBack_DelegatesToSelectedChild() { + uut.ensureViewIsCreated() + Java6Assertions.assertThat(uut.handleBack(CommandListenerAdapter())).isFalse + uut.selectTab(4) + Java6Assertions.assertThat(uut.handleBack(CommandListenerAdapter())).isTrue + Mockito.verify(child5).handleBack(ArgumentMatchers.any()) + } + + @Test + fun applyChildOptions_bottomTabsOptionsAreClearedAfterApply() { + val parent = Mockito.mock(ParentController::class.java) + uut.parentController = parent + child1.options.bottomTabsOptions.backgroundColor = ThemeColour(Colour(Color.RED)) + child1.onViewWillAppear() + val optionsCaptor = ArgumentCaptor.forClass( + Options::class.java + ) + Mockito.verify(parent).applyChildOptions(optionsCaptor.capture(), ArgumentMatchers.any()) + Java6Assertions.assertThat(optionsCaptor.value.bottomTabsOptions.backgroundColor.hasValue()).isFalse + } + + @Test + fun applyOptions_bottomTabsCreateViewOnlyOnce() { + idleMainLooper() + Mockito.verify(presenter).applyOptions(ArgumentMatchers.any()) + Mockito.verify(bottomTabsContainer.bottomTabs, Mockito.times(2)) + .superCreateItems() // first time when view is created, second time when options are applied + } + + @Test + fun onSizeChanged_recreateItemsIfSizeHasChanged() { + val numberOfPreviousInvocations = 1 + bottomTabs.onSizeChanged(0, 0, 0, 0) + Mockito.verify(bottomTabs, Mockito.times(numberOfPreviousInvocations)).superCreateItems() + bottomTabs.onSizeChanged(100, 0, 0, 0) + Mockito.verify(bottomTabs, Mockito.times(numberOfPreviousInvocations)).superCreateItems() + bottomTabs.onSizeChanged(1080, 147, 0, 0) + Mockito.verify(bottomTabs, Mockito.times(numberOfPreviousInvocations + 1)).superCreateItems() + bottomTabs.onSizeChanged(1920, 147, 0, 0) + Mockito.verify(bottomTabs, Mockito.times(numberOfPreviousInvocations + 2)).superCreateItems() + Mockito.`when`(bottomTabs.itemsCount).thenReturn(0) + bottomTabs.onSizeChanged(1080, 147, 0, 0) + Mockito.verify(bottomTabs, Mockito.times(numberOfPreviousInvocations + 2)).superCreateItems() + } + + @Test + fun mergeOptions_currentTabIndex() { + uut.ensureViewIsCreated() + Java6Assertions.assertThat(uut.selectedIndex).isZero + val options = Options() + options.bottomTabsOptions.currentTabIndex = Number(1) + uut.mergeOptions(options) + Java6Assertions.assertThat(uut.selectedIndex).isOne + Mockito.verify(eventEmitter, Mockito.times(0)).emitBottomTabSelected( + ArgumentMatchers.any( + Int::class.java + ), ArgumentMatchers.any(Int::class.java) + ) + } + + @Test + fun mergeOptions_drawBehind() { + Java6Assertions.assertThat(uut.getBottomInset(child1)).isEqualTo(uut.bottomTabs.height) + val o1 = Options() + o1.bottomTabsOptions.drawBehind = Bool(true) + child1.mergeOptions(o1) + Java6Assertions.assertThat(uut.getBottomInset(child1)).isEqualTo(0) + val o2 = Options() + o2.topBar.title.text = Text("Some text") + child1.mergeOptions(o1) + Java6Assertions.assertThat(uut.getBottomInset(child1)).isEqualTo(0) + } + + @Test + fun mergeOptions_drawBehind_stack() { + uut.ensureViewIsCreated() + uut.selectTab(3) + Java6Assertions.assertThat((stackChild.view.layoutParams as MarginLayoutParams).bottomMargin).isEqualTo( + bottomTabs.height + ) + val o1 = Options() + o1.bottomTabsOptions.drawBehind = Bool(true) + stackChild.mergeOptions(o1) + Java6Assertions.assertThat((stackChild.view.layoutParams as MarginLayoutParams).bottomMargin).isEqualTo(0) + } + + @Test + fun mergeOptions_mergesBottomTabOptions() { + val options = Options() + uut.mergeOptions(options) + Mockito.verify(bottomTabPresenter).mergeOptions(options) + } + + @Test + fun applyChildOptions_resolvedOptionsAreUsed() { + val childOptions = Options() + val pushedScreen = SimpleViewController(activity, childRegistry, "child4.1", childOptions) + disablePushAnimation(pushedScreen) + child4 = spyOnStack(pushedScreen) + tabs = ArrayList(listOf(child4)) + tabsAttacher = BottomTabsAttacher(tabs, presenter, Options.EMPTY) + initialOptions.bottomTabsOptions.currentTabIndex = Number(0) + val resolvedOptions = Options() + uut = object : BottomTabsController( + activity, + tabs, + childRegistry, + eventEmitter, + imageLoaderMock, + "uut", + initialOptions, + Presenter(activity, Options()), + tabsAttacher, + presenter, + BottomTabPresenter(activity, tabs, mock(), TypefaceLoaderMock(), Options()) + ) { + override fun resolveCurrentOptions(): Options { + return resolvedOptions + } + + override fun createBottomTabs(): BottomTabs { + return object : BottomTabs(activity) { + override fun createItems() {} + } + } + } + activity.setContentView(uut.getView()) + idleMainLooper() + Mockito.verify(presenter, Mockito.times(2)) + .applyChildOptions(ArgumentMatchers.eq(resolvedOptions), ArgumentMatchers.any()) + } + + @Test + fun child_mergeOptions_currentTabIndex() { + uut.ensureViewIsCreated() + Java6Assertions.assertThat(uut.selectedIndex).isZero + val options = Options() + options.bottomTabsOptions.currentTabIndex = Number(1) + child1.mergeOptions(options) + Java6Assertions.assertThat(uut.selectedIndex).isOne + } + + @Test + fun resolveCurrentOptions_returnsFirstTabIfInvokedBeforeViewIsCreated() { + uut = createBottomTabs() + Java6Assertions.assertThat(uut.currentChild).isEqualTo(tabs[0]) + } + + @Test + fun buttonPressInvokedOnCurrentTab() { + uut.ensureViewIsCreated() + uut.selectTab(4) + uut.sendOnNavigationButtonPressed("btn1") + Mockito.verify(child5, Mockito.times(1)).sendOnNavigationButtonPressed("btn1") + } + + @Test + fun push() { + uut.selectTab(3) + val stackChild2 = SimpleViewController(activity, childRegistry, "stackChild2", Options()) + disablePushAnimation(stackChild2) + TestUtils.hideBackButton(stackChild2) + Java6Assertions.assertThat(child4.size()).isEqualTo(1) + child4.push(stackChild2, CommandListenerAdapter()) + Java6Assertions.assertThat(child4.size()).isEqualTo(2) + } + + @Test + fun oneTimeOptionsAreAppliedOnce() { + val options = Options() + options.bottomTabsOptions.currentTabIndex = Number(1) + Java6Assertions.assertThat(uut.selectedIndex).isZero + uut.mergeOptions(options) + Java6Assertions.assertThat(uut.selectedIndex).isOne + Java6Assertions.assertThat(uut.options.bottomTabsOptions.currentTabIndex.hasValue()).isFalse + Java6Assertions.assertThat(uut.initialOptions.bottomTabsOptions.currentTabIndex.hasValue()).isFalse + } + + @Test + fun selectTab() { + uut.selectTab(1) + Mockito.verify(tabsAttacher).onTabSelected(tabs[1]) + } + + @Test + fun selectTab_onViewDidAppearIsInvokedAfterSelection() { + uut.selectTab(1) + Mockito.verify(child2).onViewDidAppear() + } + + @Test + fun creatingTabs_onViewDidAppearInvokedAfterInitialTabIndexSet() { + val options = Options.EMPTY.copy() + options.bottomTabsOptions.currentTabIndex = Number(1) + prepareViewsForTests(options.bottomTabsOptions) + idleMainLooper() + Mockito.verify(tabs[0], Mockito.times(0)).onViewDidAppear() + Mockito.verify(tabs[1], Mockito.times(1)).onViewDidAppear() + Mockito.verify(tabs[2], Mockito.times(0)).onViewDidAppear() + Mockito.verify(tabs[3], Mockito.times(0)).onViewDidAppear() + Mockito.verify(tabs[4], Mockito.times(0)).onViewDidAppear() + } + + @get:Test + val topInset: Unit + get() { + Java6Assertions.assertThat(child1.topInset).isEqualTo(statusBarHeight) + Java6Assertions.assertThat(child2.topInset).isEqualTo(statusBarHeight) + child1.options.statusBar.drawBehind = Bool(true) + Java6Assertions.assertThat(child1.topInset).isEqualTo(0) + Java6Assertions.assertThat(child2.topInset).isEqualTo(statusBarHeight) + Java6Assertions.assertThat(stackChild.topInset).isEqualTo(statusBarHeight + child4.topBar.height) + } + + @get:Test + val bottomInset_defaultOptionsAreTakenIntoAccount: Unit + get() { + val defaultOptions = Options() + defaultOptions.bottomTabsOptions.visible = Bool(false) + Java6Assertions.assertThat(uut.getBottomInset(child1)).isEqualTo(bottomTabs.height) + uut.setDefaultOptions(defaultOptions) + Java6Assertions.assertThat(uut.getBottomInset(child1)).isZero + } + + @Test + fun destroy() { + uut.destroy() + Mockito.verify(tabsAttacher).destroy() + } + + private fun prepareViewsForTests(bottomTabsOptions: BottomTabsOptions = initialOptions.bottomTabsOptions) { + ObjectUtils.perform(uut, { obj: BottomTabsController -> obj.destroy() }) + bottomTabs = Mockito.spy(object : BottomTabs(activity) { + override fun superCreateItems() {} + }) + bottomTabsContainer = Mockito.spy(BottomTabsContainer(activity, bottomTabs)) + createChildren() + tabs = Arrays.asList(child1, child2, child3, child4, child5) + initialOptions.bottomTabsOptions = bottomTabsOptions + presenter = Mockito.spy(BottomTabsPresenter(tabs, initialOptions, BottomTabsAnimator())) + bottomTabPresenter = + Mockito.spy(BottomTabPresenter(activity, tabs, mock(), TypefaceLoaderMock(), initialOptions)) + tabsAttacher = Mockito.spy(BottomTabsAttacher(tabs, presenter, initialOptions)) + uut = createBottomTabs() + activity.setContentView(FakeParentController(activity, childRegistry, uut).view) + } + + private fun createChildren() { + child1 = Mockito.spy(SimpleViewController(activity, childRegistry, "child1", tabOptions)) + child2 = Mockito.spy(SimpleViewController(activity, childRegistry, "child2", tabOptions)) + child3 = Mockito.spy(SimpleViewController(activity, childRegistry, "child3", tabOptions)) + stackChild = Mockito.spy(SimpleViewController(activity, childRegistry, "stackChild", tabOptions)) + child4 = spyOnStack(stackChild) + child5 = Mockito.spy(SimpleViewController(activity, childRegistry, "child5", tabOptions)) + Mockito.`when`(child5.handleBack(ArgumentMatchers.any())).thenReturn(true) + } + + private fun spyOnStack(initialChild: ViewController<*>?): StackController { + val build = TestUtils.newStackController(activity) + .setInitialOptions(tabOptions) + .build() + val stack = Mockito.spy(build) + disablePushAnimation(initialChild) + stack.ensureViewIsCreated() + stack.push(initialChild, CommandListenerAdapter()) + return stack + } + + private fun createBottomTabs(): BottomTabsController { + return object : BottomTabsController( + activity, + tabs, + childRegistry, + eventEmitter, + imageLoaderMock, + "uut", + initialOptions, + Presenter(activity, initialOptions), + tabsAttacher, + presenter, + bottomTabPresenter + ) { + override fun ensureViewIsCreated() { + super.ensureViewIsCreated() + uut.view.layout(0, 0, 1000, 1000) + } + + override fun createView(): BottomTabsLayout { + val view = super.createView() + bottomTabs.layoutParams.height = 100 + return view + } + + override fun createBottomTabsContainer(): BottomTabsContainer { + return bottomTabsContainer + } + + override fun createBottomTabs(): BottomTabs { + return bottomTabs + } + } + } + + private val statusBarHeight: Int + private get() = getStatusBarHeight(activity) +} \ No newline at end of file From 303d8c56b5170eedd67a7ed9845e3e3858ea8da7 Mon Sep 17 00:00:00 2001 From: Ward Abbass Date: Mon, 29 Nov 2021 09:10:24 +0200 Subject: [PATCH 02/14] implement flows of tab selection --- .../options/HardwareBackButtonOptions.kt | 37 +++++++++++++++++- .../bottomtabs/BottomTabsController.java | 39 ++++++++++++++++++- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/options/HardwareBackButtonOptions.kt b/lib/android/app/src/main/java/com/reactnativenavigation/options/HardwareBackButtonOptions.kt index 4aeee3225d8..1ece4e921e7 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/options/HardwareBackButtonOptions.kt +++ b/lib/android/app/src/main/java/com/reactnativenavigation/options/HardwareBackButtonOptions.kt @@ -6,10 +6,40 @@ import com.reactnativenavigation.options.parsers.BoolParser import org.json.JSONObject +sealed class HwBackBottomTabsBehaviour { + object Undefined : HwBackBottomTabsBehaviour() { + override fun hasValue(): Boolean = false + } + + object Exit : HwBackBottomTabsBehaviour() + object PrevSelection : HwBackBottomTabsBehaviour() + object JumpToFirst : HwBackBottomTabsBehaviour() + + open fun hasValue(): Boolean = true + + companion object { + private const val BEHAVIOUR_EXIT = "exit" + private const val BEHAVIOUR_PREV = "previous" + private const val BEHAVIOUR_FIRST = "first" + fun fromString(behaviour: String?): HwBackBottomTabsBehaviour { + return when (behaviour) { + BEHAVIOUR_PREV -> PrevSelection + BEHAVIOUR_FIRST -> JumpToFirst + BEHAVIOUR_EXIT -> Exit + else -> Undefined + } + } + } +} + open class HardwareBackButtonOptions(json: JSONObject? = null) { - @JvmField var dismissModalOnPress: Bool = NullBool() - @JvmField var popStackOnPress: Bool = NullBool() + @JvmField + var dismissModalOnPress: Bool = NullBool() + + @JvmField + var popStackOnPress: Bool = NullBool() + var bottomTabOnPress: HwBackBottomTabsBehaviour = HwBackBottomTabsBehaviour.Undefined init { parse(json) @@ -18,16 +48,19 @@ open class HardwareBackButtonOptions(json: JSONObject? = null) { fun mergeWith(other: HardwareBackButtonOptions) { if (other.dismissModalOnPress.hasValue()) dismissModalOnPress = other.dismissModalOnPress if (other.popStackOnPress.hasValue()) popStackOnPress = other.popStackOnPress + if (other.bottomTabOnPress.hasValue()) bottomTabOnPress = other.bottomTabOnPress } fun mergeWithDefault(defaultOptions: HardwareBackButtonOptions) { if (!dismissModalOnPress.hasValue()) dismissModalOnPress = defaultOptions.dismissModalOnPress if (!popStackOnPress.hasValue()) popStackOnPress = defaultOptions.popStackOnPress + if (!bottomTabOnPress.hasValue()) bottomTabOnPress = defaultOptions.bottomTabOnPress } private fun parse(json: JSONObject?) { json ?: return dismissModalOnPress = BoolParser.parse(json, "dismissModalOnPress") popStackOnPress = BoolParser.parse(json, "popStackOnPress") + bottomTabOnPress = HwBackBottomTabsBehaviour.fromString(json.optString("bottomTabsOnPress")) } } \ No newline at end of file diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java index 9e38012f9ed..064c411a203 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java @@ -13,6 +13,7 @@ import com.aurelhubert.ahbottomnavigation.AHBottomNavigation; import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem; import com.reactnativenavigation.options.BottomTabOptions; +import com.reactnativenavigation.options.HwBackBottomTabsBehaviour; import com.reactnativenavigation.options.Options; import com.reactnativenavigation.react.CommandListener; import com.reactnativenavigation.react.CommandListenerAdapter; @@ -29,6 +30,8 @@ import com.reactnativenavigation.views.bottomtabs.BottomTabsLayout; import java.util.Collection; +import java.util.Deque; +import java.util.LinkedList; import java.util.List; import static com.reactnativenavigation.utils.CollectionUtils.forEach; @@ -39,6 +42,7 @@ public class BottomTabsController extends ParentController imp private BottomTabsContainer bottomTabsContainer; private BottomTabs bottomTabs; + private final Deque selectionStack; private final List> tabs; private final EventEmitter eventEmitter; private final ImageLoader imageLoader; @@ -66,6 +70,7 @@ public BottomTabsController(Activity activity, List> tabs, Chi this.presenter = bottomTabsPresenter; this.tabPresenter = bottomTabPresenter; forEach(tabs, tab -> tab.setParentController(this)); + selectionStack = new LinkedList<>(); } @Override @@ -156,7 +161,27 @@ public void mergeChildOptions(Options options, ViewController child) { @Override public boolean handleBack(CommandListener listener) { - return !tabs.isEmpty() && tabs.get(bottomTabs.getCurrentItem()).handleBack(listener); + final boolean childBack = !tabs.isEmpty() && tabs.get(bottomTabs.getCurrentItem()).handleBack(listener); + final Options options = resolveCurrentOptions(); + if (!childBack) { + if (options.hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.PrevSelection) { + if (!selectionStack.isEmpty()) { + final int prevSelectedTabIndex = selectionStack.poll(); + selectTab(prevSelectedTabIndex, false); + return true; + } + } else if (options.hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.JumpToFirst) { + if(getSelectedIndex() != 0){ + selectTab(0, false); + return true; + }else{ + return false; + } + }else{ + return false; + } + } + return childBack; } @Override @@ -203,7 +228,7 @@ private List createTabs() { }); } - int getSelectedIndex() { + public int getSelectedIndex() { return bottomTabs.getCurrentItem(); } @@ -239,6 +264,16 @@ public void destroy() { @Override public void selectTab(final int newIndex) { + selectTab(newIndex, + resolveCurrentOptions().hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.PrevSelection); + } + + private void selectTab(int newIndex, boolean enableSelectionHistory) { + if (enableSelectionHistory) { + if (selectionStack.isEmpty() || selectionStack.peek() != newIndex) + selectionStack.offerFirst(bottomTabs.getCurrentItem()); + } + tabsAttacher.onTabSelected(tabs.get(newIndex)); getCurrentView().setVisibility(View.INVISIBLE); bottomTabs.setCurrentItem(newIndex, false); From e18945a1d257d0587e0299d5d405b3190a60153d Mon Sep 17 00:00:00 2001 From: Ward Abbass Date: Mon, 29 Nov 2021 09:22:23 +0200 Subject: [PATCH 03/14] Update BottomTabsControllerTest.kt --- .../bottomtabs/BottomTabsControllerTest.kt | 123 ++++++++++++------ 1 file changed, 82 insertions(+), 41 deletions(-) diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt index 75727a34c34..f351f7fcd53 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt @@ -13,12 +13,11 @@ import com.reactnativenavigation.mocks.ImageLoaderMock.mock import com.reactnativenavigation.mocks.SimpleViewController import com.reactnativenavigation.mocks.TypefaceLoaderMock import com.reactnativenavigation.options.BottomTabsOptions +import com.reactnativenavigation.options.HwBackBottomTabsBehaviour import com.reactnativenavigation.options.Options import com.reactnativenavigation.options.params.* -import com.reactnativenavigation.options.params.Number import com.reactnativenavigation.react.CommandListenerAdapter import com.reactnativenavigation.react.events.EventEmitter -import com.reactnativenavigation.utils.ObjectUtils import com.reactnativenavigation.utils.OptionHelper import com.reactnativenavigation.utils.StatusBarUtils.getStatusBarHeight import com.reactnativenavigation.utils.StatusBarUtils.saveStatusBarHeight @@ -37,6 +36,9 @@ import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.times import java.util.* class BottomTabsControllerTest : BaseTest() { @@ -60,6 +62,7 @@ class BottomTabsControllerTest : BaseTest() { private lateinit var bottomTabPresenter: BottomTabPresenter private lateinit var tabsAttacher: BottomTabsAttacher override fun beforeEach() { + super.beforeEach() activity = newActivity() childRegistry = ChildControllersRegistry() eventEmitter = Mockito.mock(EventEmitter::class.java) @@ -98,6 +101,7 @@ class BottomTabsControllerTest : BaseTest() { fun setTabs_ThrowWhenMoreThan5() { tabs.add(SimpleViewController(activity, childRegistry, "6", tabOptions)) createBottomTabs() + idleMainLooper() } @Test @@ -167,6 +171,36 @@ class BottomTabsControllerTest : BaseTest() { Mockito.verify(child5).handleBack(ArgumentMatchers.any()) } + @Test + fun `handleBack - reselect tab selection history of navigation when root has bottom tabs`() { + val options = Options().apply { + hardwareBack.bottomTabOnPress = HwBackBottomTabsBehaviour.PrevSelection + } + prepareViewsForTests(options = options) + idleMainLooper() + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(0) + + uut.selectTab(1) + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(1) + + uut.selectTab(3) + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(3) + + uut.selectTab(2) + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(2) + + Java6Assertions.assertThat(uut.handleBack(CommandListenerAdapter())).isTrue + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(3) + + Java6Assertions.assertThat(uut.handleBack(CommandListenerAdapter())).isTrue + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(1) + + Java6Assertions.assertThat(uut.handleBack(CommandListenerAdapter())).isTrue + Java6Assertions.assertThat(uut.selectedIndex).isEqualTo(0) + + Java6Assertions.assertThat(uut.handleBack(CommandListenerAdapter())).isFalse + } + @Test fun applyChildOptions_bottomTabsOptionsAreClearedAfterApply() { val parent = Mockito.mock(ParentController::class.java) @@ -183,8 +217,8 @@ class BottomTabsControllerTest : BaseTest() { @Test fun applyOptions_bottomTabsCreateViewOnlyOnce() { idleMainLooper() - Mockito.verify(presenter).applyOptions(ArgumentMatchers.any()) - Mockito.verify(bottomTabsContainer.bottomTabs, Mockito.times(2)) + Mockito.verify(presenter).applyOptions(any()) + Mockito.verify(bottomTabsContainer.bottomTabs, times(2)) .superCreateItems() // first time when view is created, second time when options are applied } @@ -285,10 +319,10 @@ class BottomTabsControllerTest : BaseTest() { } } } - activity.setContentView(uut.getView()) + activity.setContentView(uut.view) idleMainLooper() Mockito.verify(presenter, Mockito.times(2)) - .applyChildOptions(ArgumentMatchers.eq(resolvedOptions), ArgumentMatchers.any()) + .applyChildOptions(eq(resolvedOptions), any()) } @Test @@ -362,26 +396,24 @@ class BottomTabsControllerTest : BaseTest() { Mockito.verify(tabs[4], Mockito.times(0)).onViewDidAppear() } - @get:Test - val topInset: Unit - get() { - Java6Assertions.assertThat(child1.topInset).isEqualTo(statusBarHeight) - Java6Assertions.assertThat(child2.topInset).isEqualTo(statusBarHeight) - child1.options.statusBar.drawBehind = Bool(true) - Java6Assertions.assertThat(child1.topInset).isEqualTo(0) - Java6Assertions.assertThat(child2.topInset).isEqualTo(statusBarHeight) - Java6Assertions.assertThat(stackChild.topInset).isEqualTo(statusBarHeight + child4.topBar.height) - } + @Test + fun topInset() { + Java6Assertions.assertThat(child1.topInset).isEqualTo(statusBarHeight) + Java6Assertions.assertThat(child2.topInset).isEqualTo(statusBarHeight) + child1.options.statusBar.drawBehind = Bool(true) + Java6Assertions.assertThat(child1.topInset).isEqualTo(0) + Java6Assertions.assertThat(child2.topInset).isEqualTo(statusBarHeight) + Java6Assertions.assertThat(stackChild.topInset).isEqualTo(statusBarHeight + child4.topBar.height) + } - @get:Test - val bottomInset_defaultOptionsAreTakenIntoAccount: Unit - get() { - val defaultOptions = Options() - defaultOptions.bottomTabsOptions.visible = Bool(false) - Java6Assertions.assertThat(uut.getBottomInset(child1)).isEqualTo(bottomTabs.height) - uut.setDefaultOptions(defaultOptions) - Java6Assertions.assertThat(uut.getBottomInset(child1)).isZero - } + @Test + fun bottomInset_defaultOptionsAreTakenIntoAccount() { + val defaultOptions = Options() + defaultOptions.bottomTabsOptions.visible = Bool(false) + Java6Assertions.assertThat(uut.getBottomInset(child1)).isEqualTo(bottomTabs.height) + uut.setDefaultOptions(defaultOptions) + Java6Assertions.assertThat(uut.getBottomInset(child1)).isZero + } @Test fun destroy() { @@ -389,20 +421,26 @@ class BottomTabsControllerTest : BaseTest() { Mockito.verify(tabsAttacher).destroy() } - private fun prepareViewsForTests(bottomTabsOptions: BottomTabsOptions = initialOptions.bottomTabsOptions) { - ObjectUtils.perform(uut, { obj: BottomTabsController -> obj.destroy() }) + private fun prepareViewsForTests( + bottomTabsOptions: BottomTabsOptions = initialOptions.bottomTabsOptions, + options: Options = initialOptions, defaultOptions: Options = initialOptions + ) { + if(::uut.isInitialized){ + uut.destroy() + } +// ObjectUtils.perform(uut, { obj: BottomTabsController -> obj.destroy() }) bottomTabs = Mockito.spy(object : BottomTabs(activity) { override fun superCreateItems() {} }) bottomTabsContainer = Mockito.spy(BottomTabsContainer(activity, bottomTabs)) createChildren() - tabs = Arrays.asList(child1, child2, child3, child4, child5) - initialOptions.bottomTabsOptions = bottomTabsOptions - presenter = Mockito.spy(BottomTabsPresenter(tabs, initialOptions, BottomTabsAnimator())) + tabs = mutableListOf(child1, child2, child3, child4, child5) + defaultOptions.bottomTabsOptions = bottomTabsOptions + presenter = Mockito.spy(BottomTabsPresenter(tabs, defaultOptions, BottomTabsAnimator())) bottomTabPresenter = - Mockito.spy(BottomTabPresenter(activity, tabs, mock(), TypefaceLoaderMock(), initialOptions)) - tabsAttacher = Mockito.spy(BottomTabsAttacher(tabs, presenter, initialOptions)) - uut = createBottomTabs() + Mockito.spy(BottomTabPresenter(activity, tabs, mock(), TypefaceLoaderMock(), defaultOptions)) + tabsAttacher = Mockito.spy(BottomTabsAttacher(tabs, presenter, defaultOptions)) + uut = createBottomTabs(options = options, defaultOptions = defaultOptions) activity.setContentView(FakeParentController(activity, childRegistry, uut).view) } @@ -413,7 +451,7 @@ class BottomTabsControllerTest : BaseTest() { stackChild = Mockito.spy(SimpleViewController(activity, childRegistry, "stackChild", tabOptions)) child4 = spyOnStack(stackChild) child5 = Mockito.spy(SimpleViewController(activity, childRegistry, "child5", tabOptions)) - Mockito.`when`(child5.handleBack(ArgumentMatchers.any())).thenReturn(true) + Mockito.`when`(child5.handleBack(any())).thenReturn(true) } private fun spyOnStack(initialChild: ViewController<*>?): StackController { @@ -427,7 +465,10 @@ class BottomTabsControllerTest : BaseTest() { return stack } - private fun createBottomTabs(): BottomTabsController { + private fun createBottomTabs( + options: Options = initialOptions, + defaultOptions: Options = initialOptions + ): BottomTabsController { return object : BottomTabsController( activity, tabs, @@ -435,8 +476,8 @@ class BottomTabsControllerTest : BaseTest() { eventEmitter, imageLoaderMock, "uut", - initialOptions, - Presenter(activity, initialOptions), + options, + Presenter(activity, defaultOptions), tabsAttacher, presenter, bottomTabPresenter @@ -448,20 +489,20 @@ class BottomTabsControllerTest : BaseTest() { override fun createView(): BottomTabsLayout { val view = super.createView() - bottomTabs.layoutParams.height = 100 + this@BottomTabsControllerTest.bottomTabs.layoutParams.height = 100 return view } override fun createBottomTabsContainer(): BottomTabsContainer { - return bottomTabsContainer + return this@BottomTabsControllerTest.bottomTabsContainer } override fun createBottomTabs(): BottomTabs { - return bottomTabs + return this@BottomTabsControllerTest.bottomTabs } } } private val statusBarHeight: Int - private get() = getStatusBarHeight(activity) + get() = getStatusBarHeight(activity) } \ No newline at end of file From 86fbbaca50a5d1f9926ef47bd01c234dcd37dbc3 Mon Sep 17 00:00:00 2001 From: Ward Abbass Date: Mon, 29 Nov 2021 09:59:47 +0200 Subject: [PATCH 04/14] Squashed commit of the following: commit 3368cda45b92361f398c916c7376e82699bc4382 Author: Ward Abbass Date: Mon Nov 29 09:56:50 2021 +0200 Upgrade unit tests dependencies on android commit 7fe8421ee6536fe1e7b774c0059e56296a19cc32 Author: Ward Abbass Date: Mon Nov 29 09:20:55 2021 +0200 tmp commit bcf0edb15b81d7d4f809c6902eda478cb31bdcee Author: Yogev Ben David Date: Thu Nov 25 13:32:27 2021 +0200 Fix playground reanimated animations (#7375) commit 623c2393fcbc1dfd9b55a014ca023ffe5ad88bed Author: Yogev Ben David Date: Thu Nov 25 11:37:42 2021 +0200 Fix missing options processing (#7370) We missed `mergeOptions` processing on the following commands: `pop`, `popTo`, `popToRoot`, `dismissModal` and `dismissAllModals`. That resulted in a bug where using the old animations syntax didn't work. commit e0da767c9170caf7d993626747eac5d909cdb9cd Author: Yogev Ben David Date: Thu Nov 25 11:37:13 2021 +0200 Fix statusBar blur option in dark mode (#7372) commit 74beb279efddf76507b7d1159463aee32337d769 Author: Yogev Ben David Date: Thu Nov 25 11:36:56 2021 +0200 Update documentation (#7373) commit 6452157cef9c62faa2cae4d053e850ddf1d1616e Author: Yogev Ben David Date: Wed Nov 24 23:20:11 2021 +0200 Fix fast refresh crash (#7371) Closes #6176 commit 709ddb8a5c0ee15fb55efba9b446240d6770f736 Author: Yogev Ben David Date: Wed Nov 24 21:37:22 2021 +0200 Fix dismissing modals with sideMenu (#7369) Dismissing a modal that has a `sideMenu` layout exposed a bug we have where we look for the component in the top ViewController presented children, with `sideMenu` it's not enough because its children are not components. Instead, We should recursively look for that component in all of the top viewController children tree. Closes #7367 commit 5cc967bf54831eee3eb42a1cb8ed89a6cfbe581d Author: Yogev Ben David Date: Wed Nov 24 15:26:08 2021 +0200 [Navigation Mock] Fix bottomTabPressed event (#7368) --- e2e/BottomTabs.test.js | 7 ++ e2e/Modals.test.js | 9 ++ lib/android/app/build.gradle | 12 +-- .../reactnativenavigation/TestActivity.java | 5 + .../mocks/ImageLoaderMock.kt | 9 +- .../com/reactnativenavigation/mocks/Mocks.kt | 5 +- .../mocks/TypefaceLoaderMock.kt | 2 +- .../options/TransitionAnimationOptionsTest.kt | 5 +- .../utils/MotionEventTest.kt | 2 +- .../utils/UiThreadTest.java | 4 +- .../utils/UiUtilsTest.java | 2 +- .../bottomtabs/BottomTabsPresenterTest.kt | 2 +- .../ComponentViewControllerTest.java | 2 +- .../fakes/FakeParentController.kt | 2 +- .../modal/ModalAnimatorTest.kt | 2 +- .../modal/ModalPresenterTest.java | 6 +- .../viewcontrollers/modal/ModalStackTest.java | 7 +- .../navigator/RootPresenterTest.kt | 10 +- .../stack/StackAnimatorTest.kt | 2 +- .../stack/StackControllerTest.kt | 2 +- .../stack/StackPresenterTest.kt | 2 +- .../stack/TopBarControllerTest.kt | 2 +- .../views/TitleAndButtonsContainerTest.kt | 4 +- .../views/animations/BaseViewAnimatorTest.kt | 8 +- .../bottomtabs/BottomTabsContainerTest.kt | 2 +- .../views/bottomtabs/BottomTabsTest.kt | 8 +- lib/ios/RNNBridgeManager.mm | 1 - lib/ios/RNNModalManager.h | 1 - lib/ios/RNNModalManager.m | 18 +--- lib/ios/UIViewController+RNNOptions.m | 2 +- lib/src/Mock/Components/ComponentScreen.tsx | 13 ++- lib/src/Mock/Stores/EventsStore.ts | 8 ++ lib/src/Mock/mocks/NativeEventsReceiver.ts | 7 +- lib/src/commands/Commands.test.ts | 68 +++++++++++++- lib/src/commands/Commands.ts | 7 +- lib/src/commands/LayoutTreeCrawler.ts | 2 +- lib/src/commands/OptionsProcessor.test.ts | 94 +++++++++---------- lib/src/commands/OptionsProcessor.ts | 24 ++--- playground/android/gradle.properties | 1 + .../src/screens/FirstBottomTabScreen.tsx | 9 ++ playground/src/screens/LayoutsScreen.tsx | 15 ++- .../src/screens/ModalCommandsScreen.tsx | 8 ++ playground/src/screens/ModalScreen.tsx | 1 + .../sharedElementCarDealer/CarCard.tsx | 6 +- .../CarDetailsScreen.tsx | 6 +- .../sharedElementCarDealer/CarStoryScreen.tsx | 4 +- .../useDismissGesture.ts | 8 +- playground/src/testIDs.ts | 1 + website/docs/api/options-stack.mdx | 3 + 49 files changed, 283 insertions(+), 147 deletions(-) diff --git a/e2e/BottomTabs.test.js b/e2e/BottomTabs.test.js index e9bc7412c90..46c1d87789b 100644 --- a/e2e/BottomTabs.test.js +++ b/e2e/BottomTabs.test.js @@ -106,4 +106,11 @@ describe('BottomTabs', () => { await elementById(TestIDs.POP_BTN).tap(); await expect(elementById(TestIDs.BOTTOM_TABS)).toBeVisible(); }); + + it('invoke bottomTabPressed event', async () => { + await elementById(TestIDs.THIRD_TAB_BAR_BTN).tap(); + await expect(elementByLabel('BottomTabPressed')).toBeVisible(); + await elementByLabel('OK').tap(); + await expect(elementByLabel('First Tab')).toBeVisible(); + }); }); diff --git a/e2e/Modals.test.js b/e2e/Modals.test.js index 3106ea4c362..b950a9a69c8 100644 --- a/e2e/Modals.test.js +++ b/e2e/Modals.test.js @@ -203,4 +203,13 @@ describe('modal', () => { await expect(elementByLabel('Toggle declared modal')).toBeVisible(); }); + + it.e2e('dismiss modal with side menu', async () => { + await elementById(TestIDs.MODAL_COMMANDS_BTN).tap(); + await elementById(TestIDs.SHOW_SIDE_MENU_MODAL).tap(); + await expect(elementByLabel('StatusBar Options')).toBeVisible(); + await elementById(TestIDs.DISMISS_MODAL_TOPBAR_BTN).tap(); + await expect(elementByLabel('StatusBar Options')).not.toBeVisible(); + await expect(elementByLabel('Modal Commands')).toBeVisible(); + }); }); diff --git a/lib/android/app/build.gradle b/lib/android/app/build.gradle index b4b39f73837..e0a280c8b5d 100644 --- a/lib/android/app/build.gradle +++ b/lib/android/app/build.gradle @@ -190,12 +190,12 @@ dependencies { implementation 'com.facebook.react:react-native:+' // tests - testImplementation 'junit:junit:4.12' - testImplementation "org.robolectric:robolectric:4.4" - testImplementation 'org.assertj:assertj-core:3.8.0' - testImplementation 'com.squareup.assertj:assertj-android:1.1.1' - testImplementation 'org.mockito:mockito-core:3.4.0' + testImplementation 'junit:junit:4.13.2' + testImplementation "org.robolectric:robolectric:4.7.2" + testImplementation 'org.assertj:assertj-core:3.11.1' + testImplementation 'org.mockito:mockito-core:4.0.0' + testImplementation 'com.squareup.assertj:assertj-android:1.2.0' testImplementation 'org.mockito:mockito-inline:3.4.0' - testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" + testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" } diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/TestActivity.java b/lib/android/app/src/test/java/com/reactnativenavigation/TestActivity.java index 8074511d632..55d7bb0485b 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/TestActivity.java +++ b/lib/android/app/src/test/java/com/reactnativenavigation/TestActivity.java @@ -15,4 +15,9 @@ public ReactGateway getReactGateway() { public void setNavigator(Navigator navigator) { this.navigator = navigator; } + + @Override + public void onBackPressed() { + super.onBackPressed(); + } } diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt b/lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt index adc0d998b50..203f5606794 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt @@ -3,12 +3,13 @@ package com.reactnativenavigation.mocks import android.graphics.Canvas import android.graphics.ColorFilter import android.graphics.drawable.Drawable -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.doAnswer -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.whenever + import com.reactnativenavigation.utils.ImageLoader import com.reactnativenavigation.utils.ImageLoader.ImagesLoadingListener +import org.mockito.Mockito.doAnswer +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever import java.util.* object ImageLoaderMock { diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/mocks/Mocks.kt b/lib/android/app/src/test/java/com/reactnativenavigation/mocks/Mocks.kt index 515b9eb87df..70ee02e0192 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/mocks/Mocks.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/mocks/Mocks.kt @@ -1,10 +1,11 @@ package com.reactnativenavigation.mocks import android.view.ViewGroup -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.whenever + import com.reactnativenavigation.options.Options import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever object Mocks { fun viewController(): ViewController<*> { diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.kt b/lib/android/app/src/test/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.kt index b17c5c68ae8..2ab731f8d5c 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.kt @@ -1,8 +1,8 @@ package com.reactnativenavigation.mocks import android.graphics.Typeface -import com.nhaarman.mockitokotlin2.mock import com.reactnativenavigation.options.parsers.TypefaceLoader +import org.mockito.kotlin.mock class TypefaceLoaderMock() : TypefaceLoader(mock()) { private var mockTypefaces: Map? = null diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/options/TransitionAnimationOptionsTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/options/TransitionAnimationOptionsTest.kt index 38c9308981c..c8e43aead45 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/options/TransitionAnimationOptionsTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/options/TransitionAnimationOptionsTest.kt @@ -1,7 +1,6 @@ package com.reactnativenavigation.options - -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.whenever +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever import com.reactnativenavigation.BaseTest import org.assertj.core.api.Assertions.assertThat import org.json.JSONArray diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/utils/MotionEventTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/utils/MotionEventTest.kt index b83a1a5f36a..ab357ccec6f 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/utils/MotionEventTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/utils/MotionEventTest.kt @@ -4,7 +4,7 @@ import android.app.Activity import android.view.MotionEvent import android.view.View import android.widget.FrameLayout -import com.nhaarman.mockitokotlin2.mock +import org.mockito.kotlin.mock import com.reactnativenavigation.BaseTest import org.assertj.core.api.Java6Assertions.assertThat import org.junit.Test diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiThreadTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiThreadTest.java index 347ba65b6fe..db6a5fd4195 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiThreadTest.java +++ b/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiThreadTest.java @@ -13,7 +13,7 @@ public void postOnUiThread() throws Exception { Runnable task = mock(Runnable.class); ShadowLooper.pauseMainLooper(); UiThread.post(task); - verifyZeroInteractions(task); + verifyNoInteractions(task); ShadowLooper.runUiThreadTasks(); verify(task, times(1)).run(); } @@ -22,7 +22,7 @@ public void postOnUiThread() throws Exception { public void postDelayedOnUiThread() throws Exception { Runnable task = mock(Runnable.class); UiThread.postDelayed(task, 1000); - verifyZeroInteractions(task); + verifyNoInteractions(task); ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); verify(task, times(1)).run(); } diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiUtilsTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiUtilsTest.java index 20fe555d72b..230859fe434 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiUtilsTest.java +++ b/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiUtilsTest.java @@ -13,7 +13,7 @@ public class UiUtilsTest extends BaseTest { public void runOnPreDrawOnce() { View view = new View(newActivity()); Runnable task = mock(Runnable.class); - verifyZeroInteractions(task); + verifyNoInteractions(task); UiUtils.runOnPreDrawOnce(view, task); dispatchPreDraw(view); diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenterTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenterTest.kt index 1d52f949e89..3b01a96058c 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenterTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenterTest.kt @@ -3,7 +3,7 @@ package com.reactnativenavigation.viewcontrollers.bottomtabs import android.animation.AnimatorSet import android.content.res.Configuration import android.graphics.Color -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import com.reactnativenavigation.BaseTest import com.reactnativenavigation.mocks.SimpleViewController import com.reactnativenavigation.options.Options diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewControllerTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewControllerTest.java index 2c14c93e2a3..a03eb5b85ec 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewControllerTest.java +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewControllerTest.java @@ -138,7 +138,7 @@ public void mergeOptions_delegatesToPresenterIfViewIsNotShown() { Options options = new Options(); Java6Assertions.assertThat(uut.isViewShown()).isFalse(); uut.mergeOptions(options); - Mockito.verifyZeroInteractions(presenter); + Mockito.verifyNoInteractions(presenter); Mockito.when(uut.isViewShown()).thenReturn(true); uut.mergeOptions(options); diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/fakes/FakeParentController.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/fakes/FakeParentController.kt index a6811a22194..bf6a4541d0a 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/fakes/FakeParentController.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/fakes/FakeParentController.kt @@ -2,7 +2,7 @@ package com.reactnativenavigation.viewcontrollers.fakes import android.app.Activity import androidx.coordinatorlayout.widget.CoordinatorLayout -import com.nhaarman.mockitokotlin2.mock +import org.mockito.kotlin.mock import com.reactnativenavigation.options.Options import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter import com.reactnativenavigation.utils.CompatUtils diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalAnimatorTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalAnimatorTest.kt index 4ec558d0f8e..8b01f725c5c 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalAnimatorTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalAnimatorTest.kt @@ -1,7 +1,7 @@ package com.reactnativenavigation.viewcontrollers.modal import android.app.Activity -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import com.reactnativenavigation.BaseTest import com.reactnativenavigation.mocks.SimpleViewController import com.reactnativenavigation.options.* diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java index fedd2e2f773..b983153614b 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java @@ -34,7 +34,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; public class ModalPresenterTest extends BaseTest { private static final String MODAL_ID_1 = "modalId1"; @@ -109,7 +109,7 @@ public void showModal_resolvesDefaultOptions() throws JSONException { defaultOptions.animations.showModal = TransitionAnimationOptionsKt.parseTransitionAnimationOptions(disabledShowModalAnimation); uut.setDefaultOptions(defaultOptions); uut.showModal(modal1, root, new CommandListenerAdapter()); - verifyZeroInteractions(animator); + verifyNoInteractions(animator); } @Test @@ -163,7 +163,7 @@ public void showModal_waitForRender() { modal1.options.animations.showModal.setWaitForRender(new Bool(true)); uut.showModal(modal1, root, new CommandListenerAdapter()); verify(modal1).addOnAppearedListener(any()); - verifyZeroInteractions(animator); + verifyNoInteractions(animator); } @Test diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java index 81affe984a6..db22bb83ae7 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java @@ -30,7 +30,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; public class ModalStackTest extends BaseTest { @@ -152,7 +153,7 @@ public void run() { uut.dismissModal(MODAL_ID_1, root, listener); verify(onModalWillDismiss, times(0)).run(); verify(listener, times(1)).onError(anyString()); - verifyZeroInteractions(listener); + verifyNoMoreInteractions(listener); } @Test @@ -188,7 +189,7 @@ public void onSuccess(String childId) { }); uut.dismissAllModals(root, Options.EMPTY, listener); verify(listener, times(1)).onSuccess(anyString()); - verifyZeroInteractions(listener); + verifyNoMoreInteractions(listener); } @Test diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.kt index 9c9c8fd7e49..8c9879bbd6a 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.kt @@ -3,9 +3,6 @@ package com.reactnativenavigation.viewcontrollers.navigator import android.app.Activity import androidx.coordinatorlayout.widget.CoordinatorLayout import com.facebook.react.ReactInstanceManager -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.eq -import com.nhaarman.mockitokotlin2.never import com.reactnativenavigation.BaseTest import com.reactnativenavigation.TestActivity import com.reactnativenavigation.hierarchy.root.RootAnimator @@ -22,6 +19,9 @@ import org.assertj.core.api.Java6Assertions import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.never import org.robolectric.android.controller.ActivityController class RootPresenterTest : BaseTest() { @@ -76,7 +76,7 @@ class RootPresenterTest : BaseTest() { fun setRoot_doesNotAnimateByDefault() { val listener = Mockito.spy(CommandListenerAdapter()) uut.setRoot(root, null, defaultOptions, listener, reactInstanceManager) - Mockito.verifyZeroInteractions(animator) + Mockito.verifyNoInteractions(animator) Mockito.verify(listener).onSuccess(root.id) } @@ -210,7 +210,7 @@ class RootPresenterTest : BaseTest() { uut.setRoot(spy, null, defaultOptions, listener, reactInstanceManager) Mockito.verify(spy).addOnAppearedListener(any()) Java6Assertions.assertThat(spy.view.alpha).isZero() - Mockito.verifyZeroInteractions(listener) + Mockito.verifyNoInteractions(listener) spy.onViewWillAppear() idleMainLooper() Java6Assertions.assertThat(spy.view.alpha).isOne() diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackAnimatorTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackAnimatorTest.kt index 080c9718372..ffbae17532d 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackAnimatorTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackAnimatorTest.kt @@ -4,7 +4,7 @@ import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.app.Activity import android.widget.FrameLayout -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import com.reactnativenavigation.BaseTest import com.reactnativenavigation.mocks.Mocks import com.reactnativenavigation.mocks.SimpleViewController diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt index cd36ab5f353..ad7d06ea8c5 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt @@ -6,7 +6,7 @@ import android.content.Context import android.view.View import android.widget.FrameLayout import androidx.coordinatorlayout.widget.CoordinatorLayout -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import com.reactnativenavigation.BaseTest import com.reactnativenavigation.TestUtils import com.reactnativenavigation.mocks.* diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt index 333b3cd2777..5470d53cf03 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt @@ -7,7 +7,7 @@ import android.graphics.Color import android.graphics.Typeface import android.view.View import android.view.ViewGroup -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import com.reactnativenavigation.BaseTest import com.reactnativenavigation.TestUtils import com.reactnativenavigation.fakes.IconResolverFake diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt index fb95f57d8f0..37f7a3f4041 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt @@ -4,7 +4,7 @@ import android.animation.AnimatorSet import android.app.Activity import android.content.Context import android.view.View -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import com.reactnativenavigation.BaseTest import com.reactnativenavigation.fakes.IconResolverFake import com.reactnativenavigation.mocks.TitleBarButtonCreatorMock diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt index 30738ce3bae..068ea815296 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt @@ -7,8 +7,8 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout -import com.nhaarman.mockitokotlin2.spy -import com.nhaarman.mockitokotlin2.verify +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify import com.reactnativenavigation.BaseTest import com.reactnativenavigation.options.Alignment import com.reactnativenavigation.options.params.Colour diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/views/animations/BaseViewAnimatorTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/views/animations/BaseViewAnimatorTest.kt index a8917d02503..61707c45d6e 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/views/animations/BaseViewAnimatorTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/views/animations/BaseViewAnimatorTest.kt @@ -2,14 +2,14 @@ package com.reactnativenavigation.views.animations import android.animation.Animator import android.view.View -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.spy -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever import com.reactnativenavigation.BaseTest import com.reactnativenavigation.options.AnimationOptions import org.assertj.core.api.Java6Assertions.assertThat import org.junit.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever class BaseViewAnimatorTest : BaseTest() { private lateinit var uut: BaseViewAnimator diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainerTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainerTest.kt index 61fccbc4abb..134c3948d4a 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainerTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainerTest.kt @@ -6,7 +6,7 @@ import android.graphics.drawable.ColorDrawable import android.view.View import android.widget.LinearLayout import androidx.core.graphics.ColorUtils -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import com.reactnativenavigation.BaseTest import org.assertj.core.api.Assertions.assertThat import org.junit.Test diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsTest.kt b/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsTest.kt index 8fb02c1253d..d75b5e40303 100644 --- a/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsTest.kt +++ b/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsTest.kt @@ -1,9 +1,9 @@ package com.reactnativenavigation.views.bottomtabs -import com.nhaarman.mockitokotlin2.never -import com.nhaarman.mockitokotlin2.spy -import com.nhaarman.mockitokotlin2.times -import com.nhaarman.mockitokotlin2.verify +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify import com.reactnativenavigation.BaseTest import org.junit.Test diff --git a/lib/ios/RNNBridgeManager.mm b/lib/ios/RNNBridgeManager.mm index 5188fe6b977..857897ca3fc 100644 --- a/lib/ios/RNNBridgeManager.mm +++ b/lib/ios/RNNBridgeManager.mm @@ -113,7 +113,6 @@ - (void)onJavaScriptLoaded { - (void)onBridgeWillReload { dispatch_async(dispatch_get_main_queue(), ^{ [self->_overlayManager dismissAllOverlays]; - [self->_modalManager dismissAllModalsSynchronosly]; [self->_componentRegistry clear]; UIApplication.sharedApplication.delegate.window.rootViewController = nil; }); diff --git a/lib/ios/RNNModalManager.h b/lib/ios/RNNModalManager.h index 07b431b837d..bd39c7a70f2 100644 --- a/lib/ios/RNNModalManager.h +++ b/lib/ios/RNNModalManager.h @@ -22,7 +22,6 @@ typedef void (^RNNTransitionRejectionBlock)(NSString *_Nonnull code, NSString *_ animated:(BOOL)animated completion:(RNNTransitionCompletionBlock _Nullable)completion; - (void)dismissAllModalsAnimated:(BOOL)animated completion:(void (^__nullable)(void))completion; -- (void)dismissAllModalsSynchronosly; - (void)reset; diff --git a/lib/ios/RNNModalManager.m b/lib/ios/RNNModalManager.m index cf6d6ae4201..074afa59fa4 100644 --- a/lib/ios/RNNModalManager.m +++ b/lib/ios/RNNModalManager.m @@ -130,21 +130,6 @@ - (void)dismissAllModalsAnimated:(BOOL)animated completion:(void (^__nullable)(v completion(); } -- (void)dismissAllModalsSynchronosly { - if (_presentedModals.count) { - dispatch_semaphore_t sem = dispatch_semaphore_create(0); - [self dismissAllModalsAnimated:NO - completion:^{ - dispatch_semaphore_signal(sem); - }]; - - while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode - beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]]; - } - } -} - - (void)reset { [_presentedModals removeAllObjects]; [_pendingModalIdsToDismiss removeAllObjects]; @@ -177,8 +162,7 @@ - (void)removePendingNextModalIfOnTop:(RNNTransitionCompletionBlock)completion _dismissModalTransitionDelegate; } - if ((modalToDismiss == topPresentedVC || - [[topPresentedVC childViewControllers] containsObject:modalToDismiss])) { + if ((modalToDismiss == topPresentedVC || [topPresentedVC findViewController:modalToDismiss])) { [self dismissSearchController:modalToDismiss]; [modalToDismiss dismissViewControllerAnimated:animated diff --git a/lib/ios/UIViewController+RNNOptions.m b/lib/ios/UIViewController+RNNOptions.m index d19b9699c39..157d81404d7 100644 --- a/lib/ios/UIViewController+RNNOptions.m +++ b/lib/ios/UIViewController+RNNOptions.m @@ -134,7 +134,7 @@ - (void)setStatusBarBlur:(BOOL)blur { if (blur) { if (!curBlurView) { UIVisualEffectView *blur = [[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular]]; blur.frame = [[UIApplication sharedApplication] statusBarFrame]; blur.tag = BLUR_STATUS_TAG; [self.view addSubview:blur]; diff --git a/lib/src/Mock/Components/ComponentScreen.tsx b/lib/src/Mock/Components/ComponentScreen.tsx index 6c5eb8e8013..62e21bff0fd 100644 --- a/lib/src/Mock/Components/ComponentScreen.tsx +++ b/lib/src/Mock/Components/ComponentScreen.tsx @@ -6,6 +6,8 @@ import { VISIBLE_SCREEN_TEST_ID } from '../constants'; import { LayoutStore } from '../Stores/LayoutStore'; import { connect } from '../connect'; import { TopBar } from './TopBar'; +import { events } from '../Stores/EventsStore'; +import _ from 'lodash'; export const ComponentScreen = connect( class extends Component { @@ -34,7 +36,13 @@ export const ComponentScreen = connect(