diff --git a/lib/android/app/build.gradle b/lib/android/app/build.gradle
index a2bdded3a6c..b94bed3c335 100644
--- a/lib/android/app/build.gradle
+++ b/lib/android/app/build.gradle
@@ -39,6 +39,8 @@ android {
defaultConfig {
minSdkVersion safeExtGetFallbackLowerBound('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
targetSdkVersion safeExtGetFallbackLowerBound('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
@@ -53,6 +55,15 @@ android {
}
testOptions {
+ managedDevices {
+ localDevices {
+ pixel3aapi34 {
+ device = "Pixel 3a"
+ apiLevel = 34
+ systemImageSource = "aosp-atd"
+ }
+ }
+ }
unitTests.includeAndroidResources = true
unitTests.all { t ->
maxHeapSize = "4g"
@@ -102,6 +113,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+'
+
if ("Playground".toLowerCase() == rootProject.name.toLowerCase()) {
// tests only for our playground
testImplementation 'junit:junit:4.13.2'
@@ -112,5 +124,22 @@ dependencies {
testImplementation 'org.mockito:mockito-inline:4.6.1'
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion"
+
+ // Core testing libraries
+ androidTestImplementation "androidx.test.ext:junit:1.2.1"
+ androidTestImplementation 'org.assertj:assertj-core:3.11.1'
+
+ androidTestImplementation "androidx.test.espresso:espresso-core:3.6.1"
+
+ androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.4"
+ androidTestImplementation 'org.opentest4j:opentest4j:1.2.0'
+ androidTestImplementation ("org.mockito.kotlin:mockito-kotlin:5.4.0") {
+ exclude group: 'org.mockito', module: 'mockito-core'
+ }
+
+ androidTestImplementation "androidx.test.uiautomator:uiautomator:2.3.0"
+
+ androidTestImplementation("com.facebook.react:hermes-android")
+
}
}
diff --git a/lib/android/app/src/androidTest/AndroidManifest.xml b/lib/android/app/src/androidTest/AndroidManifest.xml
new file mode 100644
index 00000000000..205fd59d4b2
--- /dev/null
+++ b/lib/android/app/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/BaseAndroidTest.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/BaseAndroidTest.kt
new file mode 100644
index 00000000000..5d79034505f
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/BaseAndroidTest.kt
@@ -0,0 +1,4 @@
+package com.reactnativenavigation
+
+abstract class BaseAndroidTest {
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestActivity.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestActivity.kt
new file mode 100644
index 00000000000..7433935e8d9
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestActivity.kt
@@ -0,0 +1,4 @@
+package com.reactnativenavigation
+
+class TestActivity : NavigationActivity() {
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestApplication.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestApplication.kt
new file mode 100644
index 00000000000..0816d839a76
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestApplication.kt
@@ -0,0 +1,45 @@
+package com.reactnativenavigation
+
+import com.facebook.react.ReactHost
+import com.facebook.react.ReactNativeHost
+import com.facebook.react.ReactPackage
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
+import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
+import com.facebook.react.shell.MainReactPackage
+import com.reactnativenavigation.react.NavigationPackage
+import com.reactnativenavigation.react.NavigationReactNativeHost
+
+class TestApplication : NavigationApplication() {
+
+
+ override val reactNativeHost: ReactNativeHost
+ get() = object : NavigationReactNativeHost(this) {
+ override fun getJSMainModuleName(): String {
+ return "index"
+ }
+
+ override fun getUseDeveloperSupport(): Boolean {
+ return false
+ }
+
+ public override fun getPackages(): List {
+ val packages = listOf(MainReactPackage(null), NavigationPackage())
+ return packages
+ }
+
+ override val isHermesEnabled: Boolean
+ get() = true
+
+ override val isNewArchEnabled: Boolean
+ get() = true
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ load()
+ }
+
+ override val reactHost: ReactHost
+ get() = getDefaultReactHost(this, reactNativeHost)
+
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestUtils.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestUtils.java
new file mode 100644
index 00000000000..09b7ec8b9fa
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/TestUtils.java
@@ -0,0 +1,65 @@
+package com.reactnativenavigation;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
+import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
+import com.reactnativenavigation.mocks.TitleBarButtonCreatorMock;
+import com.reactnativenavigation.mocks.TypefaceLoaderMock;
+import com.reactnativenavigation.options.Options;
+import com.reactnativenavigation.options.params.Bool;
+import com.reactnativenavigation.utils.RenderChecker;
+import com.reactnativenavigation.viewcontrollers.stack.StackPresenter;
+import com.reactnativenavigation.react.events.EventEmitter;
+import com.reactnativenavigation.utils.CompatUtils;
+import com.reactnativenavigation.utils.ImageLoader;
+import com.reactnativenavigation.utils.UiUtils;
+import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
+import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
+import com.reactnativenavigation.viewcontrollers.stack.topbar.button.IconResolver;
+import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
+import com.reactnativenavigation.viewcontrollers.stack.topbar.TopBarController;
+import com.reactnativenavigation.views.stack.StackLayout;
+import com.reactnativenavigation.views.stack.topbar.TopBar;
+
+import org.mockito.Mockito;
+
+public class TestUtils {
+ public static StackControllerBuilder newStackController(Activity activity) {
+ TopBarController topBarController = new TopBarController() {
+ @Override
+ protected TopBar createTopBar(@NonNull Context context, @NonNull StackLayout stackLayout) {
+ TopBar topBar = super.createTopBar(context, stackLayout);
+ topBar.layout(0, 0, 1000, UiUtils.getTopBarHeight(context));
+ return topBar;
+ }
+ };
+ return new StackControllerBuilder(activity, Mockito.mock(EventEmitter.class))
+ .setId("stack" + CompatUtils.generateViewId())
+ .setChildRegistry(new ChildControllersRegistry())
+ .setTopBarController(topBarController)
+ .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(),
+ new TopBarBackgroundViewCreatorMock(), new TitleBarButtonCreatorMock(),
+ new IconResolver(activity, new ImageLoader()), new TypefaceLoaderMock(), new RenderChecker(),
+ new Options()))
+ .setInitialOptions(new Options());
+ }
+
+ public static void hideBackButton(ViewController> viewController) {
+ viewController.options.topBar.buttons.back.visible = new Bool(false);
+ }
+
+ public static T spyOn(T child) {
+ ViewGroup parent = (ViewGroup) child.getParent();
+ int indexOf = parent.indexOfChild(child);
+ parent.removeView(child);
+ T spy = Mockito.spy(child);
+ parent.addView(spy, indexOf);
+ return spy;
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/fakes/IconResolverFake.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/fakes/IconResolverFake.kt
new file mode 100644
index 00000000000..76f8b3ce0bd
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/fakes/IconResolverFake.kt
@@ -0,0 +1,8 @@
+package com.reactnativenavigation.fakes
+
+import android.app.Activity
+import com.reactnativenavigation.mocks.ImageLoaderMock
+import com.reactnativenavigation.utils.ImageLoader
+import com.reactnativenavigation.viewcontrollers.stack.topbar.button.IconResolver
+
+class IconResolverFake(activity: Activity, imageLoader: ImageLoader = ImageLoaderMock.mock()) : IconResolver(activity, imageLoader)
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/BackDrawable.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/BackDrawable.java
new file mode 100644
index 00000000000..a541454a258
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/BackDrawable.java
@@ -0,0 +1,30 @@
+package com.reactnativenavigation.mocks;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class BackDrawable extends Drawable {
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt
new file mode 100644
index 00000000000..8f4afe2c153
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt
@@ -0,0 +1,49 @@
+package com.reactnativenavigation.mocks
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.drawable.Drawable
+
+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 {
+ val mockDrawable: Drawable = object : Drawable() {
+ override fun draw(canvas: Canvas) {}
+ override fun setAlpha(alpha: Int) {}
+ override fun setColorFilter(colorFilter: ColorFilter?) {}
+ override fun getOpacity(): Int {
+ return 0
+ }
+ }
+ private val backIcon: Drawable = BackDrawable()
+
+ @JvmStatic
+ fun mock(): ImageLoader {
+ return this.mock(mockDrawable)
+ }
+
+ @JvmStatic
+ fun mock(returnDrawable: Drawable = mockDrawable): ImageLoader {
+ val imageLoader = mock()
+ doAnswer { invocation ->
+ val urlCount = (invocation.arguments[1] as Collection<*>).size
+ val drawables = Collections.nCopies(urlCount, returnDrawable)
+ (invocation.arguments[2] as ImagesLoadingListener).onComplete(drawables)
+ null
+ }.`when`(imageLoader).loadIcons(any(), any(), any())
+
+ doAnswer { invocation ->
+ (invocation.arguments[2] as ImagesLoadingListener).onComplete(returnDrawable)
+ null
+ }.`when`(imageLoader).loadIcon(any(), any(), any())
+
+ whenever(imageLoader.getBackButtonIcon(any())).thenReturn(backIcon)
+ return imageLoader
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/SimpleViewController.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/SimpleViewController.java
new file mode 100644
index 00000000000..5f5ace520c2
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/SimpleViewController.java
@@ -0,0 +1,110 @@
+package com.reactnativenavigation.mocks;
+
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+
+import com.reactnativenavigation.options.Options;
+import com.reactnativenavigation.react.ReactView;
+import com.reactnativenavigation.viewcontrollers.child.ChildController;
+import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
+import com.reactnativenavigation.viewcontrollers.component.ComponentPresenterBase;
+import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
+import com.reactnativenavigation.viewcontrollers.viewcontroller.ScrollEventListener;
+import com.reactnativenavigation.views.component.ReactComponent;
+
+public class SimpleViewController extends ChildController {
+ private final ComponentPresenterBase presenter = new ComponentPresenterBase();
+
+ public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Options options) {
+ this(activity, childRegistry, id, new Presenter(activity, new Options()), options);
+ }
+
+ public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Presenter presenter, Options options) {
+ super(activity, childRegistry, id, presenter, options);
+ }
+
+ @Override
+ public SimpleView createView() {
+ return new SimpleView(getActivity());
+ }
+
+ @Override
+ public void sendOnNavigationButtonPressed(String buttonId) {
+ getView().sendOnNavigationButtonPressed(buttonId);
+ }
+
+ @Override
+ public void destroy() {
+ if (!isDestroyed()) performOnParentController(parent -> parent.onChildDestroyed(this));
+ super.destroy();
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "SimpleViewController " + getId();
+ }
+
+ @Override
+ public int getTopInset() {
+ int statusBarInset = resolveCurrentOptions().statusBar.isHiddenOrDrawBehind() ? 0 : 63;
+ return statusBarInset + perform(getParentController(), 0, p -> p.getTopInset(this));
+ }
+
+ @Override
+ public void applyBottomInset() {
+ if (view != null) presenter.applyBottomInset(view, getBottomInset());
+ }
+
+ @Override
+ public String getCurrentComponentName() {
+ return null;
+ }
+
+ public static class SimpleView extends ReactView implements ReactComponent {
+
+ public SimpleView(@NonNull Context context) {
+ super(context, "compId", "compName");
+ }
+
+ @Override
+ public boolean isRendered() {
+ return getChildCount() >= 1;
+ }
+
+ @Override
+ public boolean isReady() {
+ return false;
+ }
+
+ @Override
+ public ReactView asView() {
+ return this;
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+
+ @Override
+ public void sendOnNavigationButtonPressed(String buttonId) {
+
+ }
+
+ @Override
+ public ScrollEventListener getScrollEventListener() {
+ return null;
+ }
+
+ @Override
+ public void dispatchTouchEventToJs(MotionEvent event) {
+
+ }
+ }
+}
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TitleBarButtonCreatorMock.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TitleBarButtonCreatorMock.java
new file mode 100644
index 00000000000..18db78b3881
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TitleBarButtonCreatorMock.java
@@ -0,0 +1,26 @@
+package com.reactnativenavigation.mocks;
+
+import android.app.Activity;
+
+import com.reactnativenavigation.options.ComponentOptions;
+import com.reactnativenavigation.react.events.ComponentType;
+import com.reactnativenavigation.views.stack.topbar.titlebar.TitleBarButtonCreator;
+import com.reactnativenavigation.views.stack.topbar.titlebar.TitleBarReactButtonView;
+
+public class TitleBarButtonCreatorMock extends TitleBarButtonCreator {
+ public TitleBarButtonCreatorMock() {
+ }
+
+ @Override
+ public TitleBarReactButtonView create(Activity activity, ComponentOptions component) {
+ return new TitleBarReactButtonView(activity, component) {
+ @Override
+ public void sendComponentStart(ComponentType type) {
+ }
+
+ @Override
+ public void sendComponentStop(ComponentType type) {
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TitleBarReactViewCreatorMock.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TitleBarReactViewCreatorMock.java
new file mode 100644
index 00000000000..13f41fbf666
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TitleBarReactViewCreatorMock.java
@@ -0,0 +1,8 @@
+package com.reactnativenavigation.mocks;
+
+import com.reactnativenavigation.views.stack.topbar.titlebar.TitleBarReactViewCreator;
+
+public class TitleBarReactViewCreatorMock extends TitleBarReactViewCreator {
+ public TitleBarReactViewCreatorMock() {
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TopBarBackgroundViewCreatorMock.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TopBarBackgroundViewCreatorMock.java
new file mode 100644
index 00000000000..e55768cc58d
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TopBarBackgroundViewCreatorMock.java
@@ -0,0 +1,8 @@
+package com.reactnativenavigation.mocks;
+
+import com.reactnativenavigation.views.stack.topbar.TopBarBackgroundViewCreator;
+
+public class TopBarBackgroundViewCreatorMock extends TopBarBackgroundViewCreator {
+ public TopBarBackgroundViewCreatorMock() {
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.java
new file mode 100644
index 00000000000..73078206c9d
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.java
@@ -0,0 +1,34 @@
+package com.reactnativenavigation.mocks;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import com.reactnativenavigation.options.parsers.TypefaceLoader;
+import org.mockito.Mockito;
+
+import java.util.Map;
+
+public class TypefaceLoaderMock extends TypefaceLoader {
+ private Map mockTypefaces;
+
+ public TypefaceLoaderMock() {
+ super(Mockito.mock(Context.class));
+ }
+
+ public TypefaceLoaderMock(Map mockTypefaces) {
+ this();
+ this.mockTypefaces = mockTypefaces;
+ }
+
+ @Override
+ public Typeface getDefaultTypeFace() {
+ return Typeface.DEFAULT;
+ }
+
+ @Override
+ public Typeface getTypeFace(String fontFamilyName, String fontStyle, String fontWeight, Typeface defaultTypeFace) {
+ if (mockTypefaces != null && mockTypefaces.containsKey(fontFamilyName)) {
+ return mockTypefaces.get(fontFamilyName);
+ }
+ return defaultTypeFace;
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/utils/ButtonPresenterTest.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/utils/ButtonPresenterTest.kt
new file mode 100644
index 00000000000..4fff18a2234
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/utils/ButtonPresenterTest.kt
@@ -0,0 +1,379 @@
+package com.reactnativenavigation.utils
+
+import android.app.Activity
+import android.app.Application
+import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.text.SpannableString
+import android.view.Menu
+import android.view.MenuItem
+import android.widget.TextView
+import androidx.appcompat.widget.ActionMenuView
+import androidx.test.espresso.Espresso
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.reactnativenavigation.BaseAndroidTest
+import com.reactnativenavigation.TestActivity
+import com.reactnativenavigation.fakes.IconResolverFake
+import com.reactnativenavigation.mocks.ImageLoaderMock.mock
+import com.reactnativenavigation.options.ButtonOptions
+import com.reactnativenavigation.options.IconBackgroundOptions
+import com.reactnativenavigation.options.params.Bool
+import com.reactnativenavigation.options.params.Colour
+import com.reactnativenavigation.options.params.Number
+import com.reactnativenavigation.options.params.Text
+import com.reactnativenavigation.options.params.ThemeColour
+import com.reactnativenavigation.viewcontrollers.stack.topbar.button.ButtonController
+import com.reactnativenavigation.viewcontrollers.stack.topbar.button.ButtonPresenter
+import com.reactnativenavigation.views.stack.topbar.titlebar.ButtonBar
+import com.reactnativenavigation.views.stack.topbar.titlebar.IconBackgroundDrawable
+import com.reactnativenavigation.views.stack.topbar.titlebar.TitleBarButtonCreator
+import org.assertj.core.api.Java6Assertions
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import java.util.Objects
+
+@RunWith(AndroidJUnit4::class)
+class ButtonPresenterTest : BaseAndroidTest() {
+ private lateinit var titleBar: ButtonBar
+ private lateinit var uut: ButtonPresenter
+ private lateinit var buttonController: ButtonController
+ private lateinit var button: ButtonOptions
+ private lateinit var activity: Activity
+
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ @Before
+ fun beforeEach() {
+ rule.scenario.onActivity { activity: TestActivity ->
+ this.activity =
+ activity
+ titleBar = ButtonBar(activity)
+ activity.setContentView(titleBar)
+ button = createButton()
+
+ val imageLoaderMock =
+ mock()
+ initUUt(imageLoaderMock)
+ }
+ }
+
+ private fun initUUt(imageLoaderMock: ImageLoader) {
+ val iconResolver = IconResolverFake(
+ activity, imageLoaderMock
+ )
+ uut = ButtonPresenter(activity, button, iconResolver)
+ buttonController = ButtonController(
+ activity,
+ uut,
+ button,
+ Mockito.mock(TitleBarButtonCreator::class.java),
+ Mockito.mock(
+ ButtonController.OnClickListener::class.java
+ )
+ )
+ }
+
+ @Test
+ fun applyOptions_buttonIsAddedToMenu() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ addButtonAndApplyOptions()
+ Java6Assertions.assertThat(findButtonView().text.toString())
+ .isEqualTo(BTN_TEXT)
+ }
+ }
+
+ @Test
+ fun applyOptions_appliesColorOnButtonTextView() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ button.color =
+ ThemeColour(Colour(Color.RED), Colour(Color.RED))
+ addButtonAndApplyOptions()
+ }
+ Espresso.onIdle()
+ Java6Assertions.assertThat(findButtonView().currentTextColor).isEqualTo(Color.RED)
+ }
+
+ @Test
+ fun applyOptions_appliesColorOnButtonTextViewOnDarkMode() {
+ val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application
+ application.resources.configuration.uiMode = Configuration.UI_MODE_NIGHT_NO
+ button.color = ThemeColour(Colour(Color.RED), Colour(Color.BLACK))
+ var menuItem: MenuItem? = null
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ menuItem = addButtonAndApplyOptions()
+ }
+ Espresso.onIdle()
+ Java6Assertions.assertThat(findButtonView().currentTextColor).isEqualTo(Color.RED)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ application.resources.configuration.uiMode = Configuration.UI_MODE_NIGHT_YES
+ uut.applyOptions(titleBar, menuItem!!) { buttonController.view }
+ }
+ Espresso.onIdle()
+ Java6Assertions.assertThat(findButtonView().currentTextColor).isEqualTo(Color.BLACK)
+ }
+
+ @Test
+ fun apply_disabledColor() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ button.enabled = Bool(false)
+ addButtonAndApplyOptions()
+ }
+ Espresso.onIdle()
+ Java6Assertions.assertThat(findButtonView().currentTextColor)
+ .isEqualTo(ButtonPresenter.DISABLED_COLOR)
+ }
+
+ @Test
+ fun applyColor_shouldChangeColor() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val menuItem = addMenuButton()
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+ val color =
+ ThemeColour(Colour(Color.RED), Colour(Color.RED))
+ uut.applyColor(titleBar, menuItem, color)
+ }
+ Espresso.onIdle()
+ Java6Assertions.assertThat(findButtonView().currentTextColor).isEqualTo(Color.RED)
+ }
+
+ @Test
+ fun applyBackgroundColor_shouldChangeBackgroundColor() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val mockD = Mockito.mock(
+ IconBackgroundDrawable::class.java
+ )
+ initUUt(mock(mockD))
+ button.enabled = Bool(true)
+ button.icon = Text("icon")
+ button.color =
+ ThemeColour(Colour(Color.RED), Colour(Color.RED))
+ val iconBackground = IconBackgroundOptions()
+ iconBackground.color = ThemeColour(
+ Colour(Color.GREEN),
+ Colour(Color.GREEN)
+ )
+ button.iconBackground = iconBackground
+ val menuItem = Mockito.spy(addMenuButton())
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+
+ Java6Assertions.assertThat((menuItem.icon as IconBackgroundDrawable).backgroundColor)
+ .isEqualTo(Color.GREEN)
+
+ uut.applyBackgroundColor(
+ titleBar, menuItem,
+ ThemeColour(
+ Colour(Color.BLACK),
+ Colour(Color.BLACK)
+ )
+ )
+ Java6Assertions.assertThat((menuItem.icon as IconBackgroundDrawable).backgroundColor)
+ .isEqualTo(Color.BLACK)
+ }
+ }
+
+ @Test
+ fun applyOptions_shouldChangeIconColorTint() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val mockD = Mockito.mock(
+ IconBackgroundDrawable::class.java
+ )
+ initUUt(mock(mockD))
+ button.enabled = Bool(true)
+ button.icon = Text("icon")
+ button.color =
+ ThemeColour(Colour(Color.RED), Colour(Color.RED))
+ val menuItem = Mockito.spy(addMenuButton())
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+
+ val icon = menuItem.icon
+ Java6Assertions.assertThat(
+ icon
+ ).isNotNull()
+ Mockito.verify(icon)?.colorFilter =
+ PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN)
+ }
+ }
+
+ @Test
+ fun applyOptions_shouldChangeIconDisabledColorTint() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val mockD = Mockito.mock(
+ IconBackgroundDrawable::class.java
+ )
+ initUUt(mock(mockD))
+ button.enabled = Bool(false)
+ button.icon = Text("icon")
+ button.color =
+ ThemeColour(Colour(Color.RED), Colour(Color.RED))
+ button.disabledColor = ThemeColour(
+ Colour(Color.YELLOW),
+ Colour(Color.YELLOW)
+ )
+ val menuItem = Mockito.spy(addMenuButton())
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+
+ val icon = menuItem.icon
+ Java6Assertions.assertThat(
+ icon
+ ).isNotNull()
+ Mockito.verify(icon)?.colorFilter =
+ PorterDuffColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_IN)
+ }
+ }
+
+ @Test
+ fun applyOptions_shouldChangeIconColorBackground() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val mockD = Mockito.mock(
+ IconBackgroundDrawable::class.java
+ )
+ initUUt(mock(mockD))
+ button.enabled = Bool(true)
+ button.icon = Text("icon")
+ button.color =
+ ThemeColour(Colour(Color.RED), Colour(Color.RED))
+ val iconBackground = IconBackgroundOptions()
+ iconBackground.color = ThemeColour(
+ Colour(Color.GREEN),
+ Colour(Color.GREEN)
+ )
+ button.iconBackground = iconBackground
+ val menuItem = Mockito.spy(addMenuButton())
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+
+ val icon = menuItem.icon
+ Java6Assertions.assertThat(
+ icon
+ ).isNotNull()
+ Java6Assertions.assertThat(
+ icon
+ ).isInstanceOf(
+ IconBackgroundDrawable::class.java
+ )
+ val modifed = icon as IconBackgroundDrawable?
+ Mockito.verify(modifed!!.getWrappedDrawable()).colorFilter =
+ PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN)
+ Java6Assertions.assertThat(modifed.backgroundColor)
+ .isEqualTo(Color.GREEN)
+ }
+ }
+
+ @Test
+ fun applyOptions_shouldChangeIconDisabledColorBackground() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val mockD = Mockito.mock(
+ IconBackgroundDrawable::class.java
+ )
+ initUUt(mock(mockD))
+ button.enabled = Bool(false)
+ button.icon = Text("icon")
+ button.color =
+ ThemeColour(Colour(Color.RED), Colour(Color.RED))
+ button.disabledColor = ThemeColour(
+ Colour(Color.YELLOW),
+ Colour(Color.YELLOW)
+ )
+ val iconBackground = IconBackgroundOptions()
+ iconBackground.color = ThemeColour(
+ Colour(Color.GREEN),
+ Colour(Color.GREEN)
+ )
+ iconBackground.disabledColor = ThemeColour(
+ Colour(Color.CYAN),
+ Colour(Color.CYAN)
+ )
+ button.iconBackground = iconBackground
+ val menuItem = Mockito.spy(addMenuButton())
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+
+ val icon = menuItem.icon
+ Java6Assertions.assertThat(
+ icon
+ ).isNotNull()
+ Java6Assertions.assertThat(
+ icon
+ ).isInstanceOf(
+ IconBackgroundDrawable::class.java
+ )
+ val modifed = icon as IconBackgroundDrawable?
+ Mockito.verify(modifed!!.getWrappedDrawable()).colorFilter =
+ PorterDuffColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_IN)
+ Java6Assertions.assertThat(modifed.backgroundColor)
+ .isEqualTo(Color.CYAN)
+ }
+ }
+
+ @Test
+ fun applyColor_shouldChangeDisabledColor() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ button.enabled = Bool(false)
+ val menuItem = addMenuButton()
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+ val disabledColor = ThemeColour(
+ Colour(Color.BLUE),
+ Colour(Color.BLUE)
+ )
+ uut.applyDisabledColor(titleBar, menuItem, disabledColor)
+ }
+ Espresso.onIdle()
+ Java6Assertions.assertThat(findButtonView().currentTextColor).isEqualTo(Color.BLUE)
+ }
+
+ @Test
+ fun apply_allCaps() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ button.allCaps = Bool(false)
+ addButtonAndApplyOptions()
+ Java6Assertions.assertThat(findButtonView().isAllCaps)
+ .isEqualTo(false)
+ }
+ }
+
+ private fun addButtonAndApplyOptions(): MenuItem {
+ val menuItem = addMenuButton()
+ uut.applyOptions(titleBar, menuItem) { buttonController.view }
+ return menuItem
+ }
+
+ private fun addMenuButton(): MenuItem {
+ return titleBar.addButton(
+ Menu.NONE,
+ 1,
+ 0,
+ SpannableString.valueOf(button.text["text"])
+ ) ?: throw IllegalStateException("MenuItem should not be null")
+ }
+
+ private fun findButtonView(): TextView {
+ return ViewUtils.findChildrenByClass(
+ Objects.requireNonNull(
+ ViewUtils.findChildByClass(
+ titleBar,
+ ActionMenuView::class.java
+ )
+ ),
+ TextView::class.java
+ ) { child: Any? -> true }[0] as TextView
+ }
+
+ private fun createButton(): ButtonOptions {
+ val b = ButtonOptions()
+ b.id = "btn1"
+ b.text = Text(BTN_TEXT)
+ b.showAsAction = Number(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ return b
+ }
+
+ companion object {
+ private const val BTN_TEXT = "button1"
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AfterInitialTabTest.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AfterInitialTabTest.kt
new file mode 100644
index 00000000000..b97124c01ab
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AfterInitialTabTest.kt
@@ -0,0 +1,42 @@
+package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes
+
+import androidx.test.espresso.Espresso
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+
+class AfterInitialTabTest : AttachModeTest() {
+
+ @Before
+ override fun setup() {
+ super.setup()
+ uut = AfterInitialTab(parent, tabs, presenter, options)
+ }
+
+ @Test
+ fun attach_initialTabIsAttached() {
+ uut.attach()
+ assertIsChild(parent, tab2)
+ }
+
+ @Test
+ fun attach_otherTabsAreAttachedAfterInitialTab() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ uut.attach()
+
+ assertNotChildOf(parent, *otherTabs())
+
+ initialTab().onViewWillAppear()
+ }
+ Espresso.onIdle()
+ assertIsChild(parent, *otherTabs())
+ }
+
+ @Test
+ fun destroy() {
+ uut.destroy()
+ verify(initialTab()).removeOnAppearedListener(any())
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AttachModeTest.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AttachModeTest.kt
new file mode 100644
index 00000000000..cc1ea18ff5d
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AttachModeTest.kt
@@ -0,0 +1,98 @@
+package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.reactnativenavigation.TestActivity
+import com.reactnativenavigation.mocks.SimpleViewController
+import com.reactnativenavigation.options.Options
+import com.reactnativenavigation.options.params.Number
+import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsPresenter
+import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry
+import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController
+import com.reactnativenavigation.views.bottomtabs.BottomTabsBehaviour
+import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.spy
+
+@RunWith(AndroidJUnit4::class)
+abstract class AttachModeTest {
+ private val INITIAL_TAB = 1
+
+ private lateinit var childRegistry: ChildControllersRegistry
+ protected lateinit var parent: ViewGroup
+ protected lateinit var tab1: ViewController<*>
+ protected lateinit var tab2: ViewController<*>
+ protected lateinit var tabs: List>
+ protected lateinit var options: Options
+ protected lateinit var presenter: BottomTabsPresenter
+ protected lateinit var uut: AttachMode
+
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ @Before
+ open fun setup() {
+ rule.scenario.onActivity { activity ->
+ childRegistry = ChildControllersRegistry()
+ parent = CoordinatorLayout(activity)
+ tabs = createTabs(activity)
+ }
+ options = Options()
+ options.bottomTabsOptions.currentTabIndex = Number(INITIAL_TAB)
+ presenter = Mockito.mock(BottomTabsPresenter::class.java)
+ }
+
+ @Test
+ fun attach_layoutOptionsAreApplied() {
+ uut.attach(tab1)
+ val lp = tab1.getView().layoutParams as CoordinatorLayout.LayoutParams
+ assertThat(lp).isNotNull
+ assertThat(lp.behavior).isInstanceOf(BottomTabsBehaviour::class.java)
+ }
+
+ @Test
+ fun attach_initialTabIsVisible() {
+ uut.attach(initialTab())
+ assertThat(initialTab().getView().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun attach_otherTabsAreInvisibleWhenAttached() {
+ otherTabs().forEach { t -> uut.attach(t) }
+ otherTabs().forEach { t -> assertThat(t.getView().visibility).isEqualTo(View.INVISIBLE) }
+ }
+
+ protected fun otherTabs(): Array> {
+ return tabs.filter { t -> t != initialTab() }.toTypedArray()
+ }
+
+ protected fun initialTab(): ViewController<*> {
+ return tabs[INITIAL_TAB]
+ }
+
+ private fun createTabs(activity: android.app.Activity): List> {
+ tab1 = SimpleViewController(activity, childRegistry, "child1", Options())
+ tab2 = spy(SimpleViewController(activity, childRegistry, "child2", Options()))
+ val tab3 = SimpleViewController(activity, childRegistry, "child3", Options())
+ return listOf(tab1, tab2, tab3)
+ }
+
+ protected fun assertIsChild(parent: ViewGroup, vararg children: ViewController<*>) {
+ children.forEach { child ->
+ assertThat(child.getView().parent).isEqualTo(parent)
+ }
+ }
+
+ protected fun assertNotChildOf(parent: ViewGroup, vararg children: ViewController<*>) {
+ children.forEach { child ->
+ assertThat(child.getView().parent).isNotEqualTo(parent)
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/OnSwitchToTabTest.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/OnSwitchToTabTest.kt
new file mode 100644
index 00000000000..f2dd97f7955
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/OnSwitchToTabTest.kt
@@ -0,0 +1,32 @@
+package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes
+
+import org.junit.Before
+import org.junit.Test
+
+class OnSwitchToTabTest : AttachModeTest() {
+
+ @Before
+ override fun setup() {
+ super.setup()
+ uut = OnSwitchToTab(parent, tabs, presenter, options)
+ }
+
+ @Test
+ fun attach_onlyInitialTabIsAttached() {
+ uut.attach()
+ assertIsChild(parent, initialTab())
+ assertNotChildOf(parent, *otherTabs())
+ }
+
+ @Test
+ fun onTabSelected_initialTabIsNotHandled() {
+ uut.onTabSelected(initialTab())
+ assertNotChildOf(parent, initialTab())
+ }
+
+ @Test
+ fun onTabSelected_otherTabIsAttached() {
+ uut.onTabSelected(tab1)
+ assertIsChild(parent, tab1)
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/TogetherTest.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/TogetherTest.kt
new file mode 100644
index 00000000000..f3d900c64ee
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/TogetherTest.kt
@@ -0,0 +1,19 @@
+package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes
+
+import org.junit.Before
+import org.junit.Test
+
+class TogetherTest : AttachModeTest() {
+
+ @Before
+ override fun setup() {
+ super.setup()
+ uut = Together(parent, tabs, presenter, options)
+ }
+
+ @Test
+ fun attach_allTabsAreAttached() {
+ uut.attach()
+ assertIsChild(parent, *tabs.toTypedArray())
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java
new file mode 100644
index 00000000000..73f8cef0faf
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java
@@ -0,0 +1,102 @@
+package com.reactnativenavigation.viewcontrollers.child;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.core.app.ActivityScenario;
+
+import com.reactnativenavigation.BaseAndroidTest;
+import com.reactnativenavigation.TestActivity;
+import com.reactnativenavigation.mocks.SimpleViewController;
+import com.reactnativenavigation.options.Options;
+import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
+import com.reactnativenavigation.viewcontrollers.parent.ParentController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@RunWith(AndroidJUnit4.class)
+public class ChildControllerTest extends BaseAndroidTest {
+ private ChildController> uut;
+ private ChildControllersRegistry childRegistry;
+ private Presenter presenter;
+ private Options resolvedOptions = new Options();
+
+ @Rule
+ public ActivityScenarioRule rule = new ActivityScenarioRule<>(TestActivity.class);
+
+ @Before
+ public void beforeEach() {
+ rule.getScenario().onActivity(activity -> {
+ childRegistry = spy(new ChildControllersRegistry());
+ presenter = Mockito.mock(Presenter.class);
+ uut = new SimpleViewController(activity, childRegistry, "childId", presenter, new Options()) {
+ @Override
+ public Options resolveCurrentOptions() {
+ return resolvedOptions;
+ }
+ };
+ ParentController> parent = Mockito.mock(ParentController.class);
+ Mockito.when(parent.resolveChildOptions(uut)).thenReturn(Options.EMPTY);
+ uut.setParentController(parent);
+ });
+ }
+
+ @Test
+ public void onViewAppeared() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> uut.onViewWillAppear());
+ verify(childRegistry, times(1)).onViewAppeared(uut);
+ }
+
+ @Test
+ public void onViewDisappear() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ uut.onViewWillAppear();
+ uut.onViewDisappear();
+ });
+ verify(childRegistry, times(1)).onViewDisappear(uut);
+ }
+
+ @Test
+ public void mergeOptions() {
+ try (ActivityScenario scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(activity -> {
+ activity.setContentView(uut.getView());
+ Options options = new Options();
+ uut.mergeOptions(options);
+ verify(presenter).mergeOptions(uut, options);
+ });
+ }
+ }
+
+ @Test
+ public void mergeOptions_emptyOptionsAreIgnored() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ uut.mergeOptions(Options.EMPTY);
+ verify(presenter, times(0)).mergeOptions(any(), any());
+ });
+ }
+
+ @Test
+ public void mergeOptions_mergeWithParentViewController() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ Options options = new Options();
+ uut.mergeOptions(options);
+ verify(uut.getParentController()).mergeChildOptions(options, uut);
+ });
+ }
+
+ @Test
+ public void destroy() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> uut.destroy());
+ verify(childRegistry).onChildDestroyed(uut);
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/child/ChildControllersRegistryTest.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/child/ChildControllersRegistryTest.java
new file mode 100644
index 00000000000..c818e91219b
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/child/ChildControllersRegistryTest.java
@@ -0,0 +1,67 @@
+package com.reactnativenavigation.viewcontrollers.child;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.reactnativenavigation.BaseAndroidTest;
+import com.reactnativenavigation.TestActivity;
+import com.reactnativenavigation.mocks.SimpleViewController;
+import com.reactnativenavigation.options.Options;
+
+import org.assertj.core.api.Java6Assertions;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class ChildControllersRegistryTest extends BaseAndroidTest {
+ private ChildControllersRegistry uut;
+ private ChildController> child1;
+ private ChildController> child2;
+
+ @Rule
+ public ActivityScenarioRule rule = new ActivityScenarioRule<>(TestActivity.class);
+
+
+ @Before
+ public void beforeEach() {
+ rule.getScenario().onActivity(activity -> {
+ uut = new ChildControllersRegistry();
+ child1 = Mockito.spy(new SimpleViewController(activity, uut, "child1", new Options()));
+ child2 = Mockito.spy(new SimpleViewController(activity, uut, "child2", new Options()));
+ });
+ }
+
+ @Test
+ public void onViewAppeared() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> child1.onViewWillAppear());
+ Mockito.verify(child1, Mockito.times(0)).onViewBroughtToFront();
+ Java6Assertions.assertThat(uut.size()).isOne();
+ }
+
+ @Test
+ public void onViewDisappear() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ child1.onViewWillAppear();
+ child2.onViewWillAppear();
+ });
+ Java6Assertions.assertThat(uut.size()).isEqualTo(2);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> child2.onViewDisappear());
+ Mockito.verify(child1, Mockito.times(1)).onViewBroughtToFront();
+ Java6Assertions.assertThat(uut.size()).isOne();
+ }
+
+ @Test
+ public void onChildDestroyed() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> child1.destroy());
+ Java6Assertions.assertThat(uut.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void onViewDisappear_doesNotCrashIfNoViewsHaveAppeared() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> uut.onViewDisappear(child1));
+ }
+}
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/parent/ParentControllerTest.java b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/parent/ParentControllerTest.java
new file mode 100644
index 00000000000..fb2337a059d
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/viewcontrollers/parent/ParentControllerTest.java
@@ -0,0 +1,349 @@
+package com.reactnativenavigation.viewcontrollers.parent;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.reactnativenavigation.BaseAndroidTest;
+import com.reactnativenavigation.TestActivity;
+import com.reactnativenavigation.TestUtils;
+import com.reactnativenavigation.mocks.SimpleViewController;
+import com.reactnativenavigation.options.Options;
+import com.reactnativenavigation.options.params.Text;
+import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
+import com.reactnativenavigation.react.CommandListenerAdapter;
+import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
+import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+
+import static com.reactnativenavigation.utils.CollectionUtils.*;
+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.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class ParentControllerTest extends BaseAndroidTest {
+ private static final String INITIAL_TITLE = "initial title";
+ private Activity activity;
+ private ChildControllersRegistry childRegistry;
+ private List> children;
+ private ParentController> uut;
+ private Presenter presenter;
+
+ @Rule
+ public ActivityScenarioRule rule = new ActivityScenarioRule<>(TestActivity.class);
+
+ @Before
+ public void beforeEach() {
+ rule.getScenario().onActivity(activity -> {
+ this.activity = activity;
+ childRegistry = new ChildControllersRegistry();
+ children = new ArrayList<>();
+ Options initialOptions = new Options();
+ initialOptions.topBar.title.text = new Text(INITIAL_TITLE);
+ presenter = spy(new Presenter(activity, new Options()));
+ uut = spy(new ParentController(activity, childRegistry, "uut", presenter, initialOptions) {
+ @Override
+ public ViewController> getCurrentChild() {
+ return children.get(0);
+ }
+
+ @NonNull
+ @Override
+ public ViewGroup createView() {
+ FrameLayout layout = new FrameLayout(activity);
+ for (ViewController> child : children) {
+ child.setParentController(this);
+ layout.addView(child.getView());
+ }
+ return layout;
+ }
+
+ @Override
+ public void sendOnNavigationButtonPressed(String buttonId) {
+ }
+
+ @NonNull
+ @Override
+ public Collection> getChildControllers() {
+ return children;
+ }
+ });
+ });
+ }
+
+ @Test
+ public void onConfigurationChange_shouldCallConfigurationChangeForPresenterAndVisibleChild() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ SimpleViewController child1 = spy(
+ new SimpleViewController(activity, childRegistry, "child1", new Options()));
+ SimpleViewController child2 = spy(
+ new SimpleViewController(activity, childRegistry, "child2", new Options()));
+ children.add(child1);
+ children.add(child2);
+ Mockito.when(child1.isViewShown()).thenReturn(true);
+ Mockito.when(child2.isViewShown()).thenReturn(false);
+ uut.onConfigurationChanged(mock(Configuration.class));
+ verify(presenter).onConfigurationChanged(any(), any());
+ verify(child1).onConfigurationChanged(any());
+ verify(child2, never()).onConfigurationChanged(any());
+ });
+ }
+
+ @Test
+ public void onViewDidAppearShouldCallCurrentChildDidAppear() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ SimpleViewController child1 = spy(
+ new SimpleViewController(activity, childRegistry, "child1", new Options()));
+ SimpleViewController child2 = spy(
+ new SimpleViewController(activity, childRegistry, "child2", new Options()));
+ children.add(child1);
+ children.add(child2);
+
+ uut.onViewDidAppear();
+
+ verify(child1).onViewDidAppear();
+ verify(child2, never()).onViewDidAppear();
+ });
+ }
+
+ @Test
+ public void onViewDisappearShouldCallCurrentChildDisAppear() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ SimpleViewController child1 = spy(
+ new SimpleViewController(activity, childRegistry, "child1", new Options()));
+ SimpleViewController child2 = spy(
+ new SimpleViewController(activity, childRegistry, "child2", new Options()));
+ children.add(child1);
+ children.add(child2);
+
+ uut.onViewDisappear();
+
+ verify(child1).onViewDisappear();
+ verify(child2, never()).onViewDisappear();
+ });
+ }
+
+ @Test
+ public void holdsViewGroup() {
+ assertThat(uut.getView()).isInstanceOf(ViewGroup.class);
+ }
+
+ @Test
+ public void mustHaveChildControllers() {
+ assertThat(uut.getChildControllers()).isNotNull();
+ }
+
+ @Test
+ public void findControllerById_ChildById() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
+ SimpleViewController child2 = new SimpleViewController(activity, childRegistry, "child2", new Options());
+ children.add(child1);
+ children.add(child2);
+
+ assertThat(uut.findController("uut")).isEqualTo(uut);
+ assertThat(uut.findController("child1")).isEqualTo(child1);
+ });
+ }
+
+ @Test
+ public void findControllerById_Recursive() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ StackController stackController = TestUtils.newStackController(activity).build();
+ stackController.ensureViewIsCreated();
+ SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
+ SimpleViewController child2 = new SimpleViewController(activity, childRegistry, "child2", new Options());
+ stackController.push(child1, new CommandListenerAdapter());
+ stackController.push(child2, new CommandListenerAdapter());
+ children.add(stackController);
+
+ assertThat(uut.findController("child2")).isEqualTo(child2);
+ });
+ }
+
+ @Test
+ public void destroy_DestroysChildren() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ ViewController> child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
+ children.add(child1);
+
+ verify(child1, times(0)).destroy();
+ uut.destroy();
+ verify(child1, times(1)).destroy();
+ });
+ }
+
+ @Test
+ public void optionsAreClearedWhenChildIsAppeared() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ StackController stackController = spy(TestUtils.newStackController(activity).build());
+ stackController.ensureViewIsCreated();
+ SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
+ stackController.push(child1, new CommandListenerAdapter());
+
+ child1.onViewWillAppear();
+ verify(stackController, times(1)).clearOptions();
+ });
+ }
+
+ @Test
+ public void mergeOptions_optionsAreMergedWhenChildAppears() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ Options options = new Options();
+ options.topBar.title.text = new Text("new title");
+ ViewController> child1 = spy(new SimpleViewController(activity, childRegistry, "child1", options));
+ children.add(child1);
+ uut.ensureViewIsCreated();
+
+ child1.ensureViewIsCreated();
+ child1.onViewWillAppear();
+ ArgumentCaptor optionsCaptor = ArgumentCaptor.forClass(Options.class);
+ verify(uut, times(1)).clearOptions();
+ verify(uut, times(1)).applyChildOptions(optionsCaptor.capture(), eq(child1));
+ assertThat(optionsCaptor.getValue().topBar.title.text.get()).isEqualTo("new title");
+ });
+ }
+
+ @Test
+ public void mergeOptions_initialParentOptionsAreNotMutatedWhenChildAppears() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ Options options = new Options();
+ options.topBar.title.text = new Text("new title");
+ ViewController> child1 = spy(new SimpleViewController(activity, childRegistry, "child1", options));
+ children.add(child1);
+
+ uut.ensureViewIsCreated();
+
+ child1.ensureViewIsCreated();
+ child1.onViewWillAppear();
+ assertThat(uut.initialOptions.topBar.title.text.get()).isEqualTo(INITIAL_TITLE);
+ });
+ }
+
+ @Test
+ public void resolveCurrentOptions_returnOptionsIfNoChildren() {
+ assertThat(uut.getChildControllers().size()).isZero();
+ assertThat(uut.resolveCurrentOptions()).isEqualTo(uut.initialOptions);
+ }
+
+ @Test
+ public void resolveCurrentOptions_mergesWithCurrentChild() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ ViewController child1 = Mockito.mock(ViewController.class);
+ when(child1.getView()).thenReturn(new FrameLayout(activity));
+ Options copiedChildOptions = spy(new Options());
+ Options childOptions = spy(new Options() {
+ @Override
+ public Options copy() {
+ return copiedChildOptions;
+ }
+ });
+ when(child1.resolveCurrentOptions()).thenReturn(childOptions);
+
+ children.add(child1);
+
+ uut.ensureViewIsCreated();
+ assertThat(uut.getCurrentChild()).isEqualTo(child1);
+ uut.resolveCurrentOptions();
+ verify(child1).resolveCurrentOptions();
+ verify(copiedChildOptions).withDefaultOptions(uut.initialOptions);
+ });
+ }
+
+ @Test
+ public void resolveCurrentOptions_withDefaultOptions() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
+ children.add(child1);
+ uut.ensureViewIsCreated();
+
+ Options defaultOptions = new Options();
+ Options currentOptions = spy(new Options());
+ ParentController> spy = spy(uut);
+ Mockito.when(spy.resolveCurrentOptions()).thenReturn(currentOptions);
+ spy.resolveCurrentOptions(defaultOptions);
+ verify(currentOptions).withDefaultOptions(defaultOptions);
+ });
+ }
+
+ @Test
+ public void applyTopInset() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ children.addAll(createChildren());
+ uut.applyTopInset();
+ forEach(children, c -> verify(c).applyTopInset());
+ });
+ }
+
+ @Test
+ public void getTopInset() {
+ assertThat(uut.getTopInset()).isZero();
+ }
+
+ @Test
+ public void getTopInsetForChild() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ ParentController> parent = Mockito.mock(ParentController.class);
+ Mockito.when(parent.resolveChildOptions(uut)).thenReturn(Options.EMPTY);
+
+ when(parent.getTopInset(any())).thenReturn(123);
+ uut.setParentController(parent);
+
+ assertThat(uut.getTopInset(Mockito.mock(ViewController.class))).isEqualTo(123);
+ });
+ }
+
+ @Test
+ public void applyBottomInset() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ children.addAll(createChildren());
+ uut.applyBottomInset();
+ forEach(children, c -> verify(c).applyBottomInset());
+ });
+ }
+
+ @Test
+ public void getBottomInsetForChild() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ ParentController> parent = Mockito.mock(ParentController.class);
+ Mockito.when(parent.resolveChildOptions(uut)).thenReturn(Options.EMPTY);
+
+ when(parent.getBottomInset(any())).thenReturn(123);
+ uut.setParentController(parent);
+
+ assertThat(uut.getBottomInset(Mockito.mock(ViewController.class))).isEqualTo(123);
+ });
+ }
+
+ private List> createChildren() {
+ return Arrays.asList(
+ spy(new SimpleViewController(activity, childRegistry, "child1", new Options())),
+ spy(new SimpleViewController(activity, childRegistry, "child2", new Options())));
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/androidTest/java/com/reactnativenavigation/views/TitleSubTitleLayoutTest.kt b/lib/android/app/src/androidTest/java/com/reactnativenavigation/views/TitleSubTitleLayoutTest.kt
new file mode 100644
index 00000000000..f369f38c0bc
--- /dev/null
+++ b/lib/android/app/src/androidTest/java/com/reactnativenavigation/views/TitleSubTitleLayoutTest.kt
@@ -0,0 +1,51 @@
+package com.reactnativenavigation.views
+
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.reactnativenavigation.TestActivity
+import com.reactnativenavigation.views.stack.topbar.titlebar.TitleSubTitleLayout
+import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val UUT_WIDTH = 1000
+private const val UUT_HEIGHT = 100
+
+@RunWith(AndroidJUnit4::class)
+class TitleSubTitleLayoutTest {
+ private val testId = "mock-testId"
+
+ private lateinit var uut: TitleSubTitleLayout
+
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ @Before
+ fun setup() {
+ rule.scenario.onActivity { activity ->
+ uut = TitleSubTitleLayout(activity)
+ activity.setContentView(FrameLayout(activity).apply {
+ addView(uut, ViewGroup.LayoutParams(UUT_WIDTH, UUT_HEIGHT))
+ })
+ }
+ }
+
+ @Test
+ fun shouldSetTestIdCanonically() {
+ uut.setTestId(testId)
+ assertThat(uut.getTitleTxtView().tag).isEqualTo("$testId.title")
+ assertThat(uut.getSubTitleTxtView().tag).isEqualTo("$testId.subtitle")
+ }
+
+ @Test
+ fun shouldClearTestId() {
+ uut.setTestId(testId)
+ uut.setTestId("")
+ assertThat(uut.getTitleTxtView().tag).isNull()
+ assertThat(uut.getSubTitleTxtView().tag).isNull()
+ }
+}
\ No newline at end of file
diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java
index c41739cb792..41863210747 100644
--- a/lib/android/app/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java
+++ b/lib/android/app/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java
@@ -25,7 +25,7 @@
import org.junit.Test;
import org.mockito.Mockito;
-@Ignore("New architecture - WIP")
+@Ignore("New architecture - failed to fix")
public class PresenterTest extends BaseTest {
private Presenter uut;
private Activity activity;
diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AfterInitialTabTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AfterInitialTabTest.java
deleted file mode 100644
index 2314519d9bc..00000000000
--- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AfterInitialTabTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes;
-
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-
-@Ignore("New architecture - WIP")
-public class AfterInitialTabTest extends AttachModeTest {
-
- @Override
- public void beforeEach() {
- super.beforeEach();
- uut = new AfterInitialTab(parent, tabs, presenter, options);
- }
-
- @Test
- public void attach_initialTabIsAttached() {
- uut.attach();
- assertIsChild(parent, tab2);
- }
-
- @Test
- public void attach_otherTabsAreAttachedAfterInitialTab() {
- uut.attach();
- assertNotChildOf(parent, otherTabs());
-
- initialTab().onViewWillAppear();
- idleMainLooper();
- assertIsChild(parent, otherTabs());
- }
-
- @Test
- public void destroy() {
- uut.destroy();
- verify(initialTab()).removeOnAppearedListener(any());
- }
-}
diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AttachModeTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AttachModeTest.java
deleted file mode 100644
index 7d708a549e6..00000000000
--- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/AttachModeTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes;
-
-import android.app.Activity;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.reactnativenavigation.BaseTest;
-import com.reactnativenavigation.mocks.SimpleViewController;
-import com.reactnativenavigation.options.Options;
-import com.reactnativenavigation.options.params.Number;
-import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsPresenter;
-import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
-import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
-import com.reactnativenavigation.views.bottomtabs.BottomTabsBehaviour;
-
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import java.util.Arrays;
-import java.util.List;
-
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-
-import static com.reactnativenavigation.utils.CollectionUtils.*;
-import static org.assertj.core.api.Java6Assertions.assertThat;
-import static org.mockito.Mockito.spy;
-
-public abstract class AttachModeTest extends BaseTest {
- private static final int INITIAL_TAB = 1;
-
- private Activity activity;
- private ChildControllersRegistry childRegistry;
- protected ViewGroup parent;
- ViewController> tab1;
- ViewController> tab2;
- List> tabs;
- protected Options options;
- protected BottomTabsPresenter presenter;
- protected AttachMode uut;
-
- @Override
- public void beforeEach() {
- super.beforeEach();
- activity = newActivity();
- childRegistry = new ChildControllersRegistry();
- parent = new CoordinatorLayout(activity);
- tabs = createTabs();
- options = new Options();
- options.bottomTabsOptions.currentTabIndex = new Number(INITIAL_TAB);
- presenter = Mockito.mock(BottomTabsPresenter.class);
- }
-
- @Test
- public void attach_layoutOptionsAreApplied() {
- uut.attach(tab1);
- CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) tab1.getView().getLayoutParams();
- assertThat(lp).isNotNull();
- assertThat(lp.getBehavior()).isInstanceOf(BottomTabsBehaviour.class);
- }
-
- @Test
- public void attach_initialTabIsVisible() {
- uut.attach(initialTab());
- assertThat(initialTab().getView().getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- public void attach_otherTabsAreInvisibleWhenAttached() {
- forEach(otherTabs(), t -> uut.attach(t));
- forEach(otherTabs(), t -> assertThat(t.getView().getVisibility()).isEqualTo(View.INVISIBLE));
- }
-
- ViewController>[] otherTabs() {
- return filter(tabs, t -> t != initialTab()).toArray(new ViewController[0]);
- }
-
- ViewController> initialTab() {
- return tabs.get(INITIAL_TAB);
- }
-
- private List> createTabs() {
- tab1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
- tab2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
- ViewController> tab3 = new SimpleViewController(activity, childRegistry, "child3", new Options());
- return Arrays.asList(tab1, tab2, tab3);
- }
-}
diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/OnSwitchToTabTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/OnSwitchToTabTest.java
deleted file mode 100644
index 1ee3a0192e8..00000000000
--- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/OnSwitchToTabTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes;
-
-import org.junit.*;
-
-@Ignore("New architecture - WIP")
-public class OnSwitchToTabTest extends AttachModeTest {
-
- @Override
- public void beforeEach() {
- super.beforeEach();
- uut = new OnSwitchToTab(parent, tabs, presenter, options);
- }
-
- @Test
- public void attach_onlyInitialTabIsAttached() {
- uut.attach();
- assertIsChild(parent, initialTab());
- assertNotChildOf(parent, otherTabs());
- }
-
- @Test
- public void onTabSelected_initialTabIsNotHandled() {
- uut.onTabSelected(initialTab());
- assertNotChildOf(parent, initialTab());
- }
-
- @Test
- public void onTabSelected_otherTabIsAttached() {
- uut.onTabSelected(tab1);
- assertIsChild(parent, tab1);
- }
-}
diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/TogetherTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/TogetherTest.java
deleted file mode 100644
index 4ef04650247..00000000000
--- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/attacher/modes/TogetherTest.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.reactnativenavigation.viewcontrollers.bottomtabs.attacher.modes;
-
-import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
-
-import org.junit.*;
-
-@Ignore("New architecture - WIP")
-public class TogetherTest extends AttachModeTest {
-
- @Override
- public void beforeEach() {
- super.beforeEach();
- uut = new Together(parent, tabs, presenter, options);
- }
-
- @Test
- public void attach_allTabsAreAttached() {
- uut.attach();
- assertIsChild(parent, tabs.toArray(new ViewController[0]));
- }
-}
diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java
deleted file mode 100644
index 7205e4b73b7..00000000000
--- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.reactnativenavigation.viewcontrollers.child;
-
-import com.reactnativenavigation.BaseTest;
-import com.reactnativenavigation.mocks.SimpleViewController;
-import com.reactnativenavigation.options.Options;
-import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
-import com.reactnativenavigation.viewcontrollers.parent.ParentController;
-
-import org.junit.Ignore;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-@Ignore("New architecture - WIP")
-public class ChildControllerTest extends BaseTest {
-
- private ChildController> uut;
- private ChildControllersRegistry childRegistry;
- private Presenter presenter;
- private Options resolvedOptions = new Options();
-
- @Override
- public void beforeEach() {
- super.beforeEach();
- childRegistry = spy(new ChildControllersRegistry());
- presenter = Mockito.mock(Presenter.class);
- uut = new SimpleViewController(newActivity(), childRegistry, "childId", presenter, new Options()) {
- @Override
- public Options resolveCurrentOptions() {
- return resolvedOptions;
- }
- };
- ParentController> parent = Mockito.mock(ParentController.class);
- Mockito.when(parent.resolveChildOptions(uut)).thenReturn(Options.EMPTY);
- uut.setParentController(parent);
- }
-
- @Test
- public void onViewAppeared() {
- uut.onViewWillAppear();
- verify(childRegistry, times(1)).onViewAppeared(uut);
- }
-
- @Test
- public void onViewDisappear() {
- uut.onViewWillAppear();
-
- uut.onViewDisappear();
- verify(childRegistry, times(1)).onViewDisappear(uut);
- }
-
- @Test
- public void mergeOptions() {
- newActivity().setContentView(uut.getView());
-
- Options options = new Options();
- uut.mergeOptions(options);
- verify(presenter).mergeOptions(uut, options);
- }
-
- @Test
- public void mergeOptions_emptyOptionsAreIgnored() {
- uut.mergeOptions(Options.EMPTY);
- verify(presenter, times(0)).mergeOptions(any(), any());
- }
-
- @Test
- public void mergeOptions_mergeWithParentViewController() {
- Options options = new Options();
- uut.mergeOptions(options);
- verify(uut.getParentController()).mergeChildOptions(options, uut);
- }
-
- @Test
- public void destroy() {
- uut.destroy();
- verify(childRegistry).onChildDestroyed(uut);
- }
-}
diff --git a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllersRegistryTest.java b/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllersRegistryTest.java
deleted file mode 100644
index c9a2a204ef5..00000000000
--- a/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllersRegistryTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.reactnativenavigation.viewcontrollers.child;
-
-import android.app.Activity;
-
-import com.reactnativenavigation.BaseTest;
-import com.reactnativenavigation.mocks.SimpleViewController;
-import com.reactnativenavigation.options.Options;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-import static org.assertj.core.api.Java6Assertions.assertThat;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-@Ignore("New architecture - WIP")
-public class ChildControllersRegistryTest extends BaseTest {
- private ChildControllersRegistry uut;
- private ChildController> child1;
- private ChildController> child2;
-
- @Override
- public void beforeEach() {
- super.beforeEach();
- Activity activity = newActivity();
- uut = new ChildControllersRegistry();
- child1 = spy(new SimpleViewController(activity, uut, "child1", new Options()));
- child2 = spy(new SimpleViewController(activity, uut, "child2", new Options()));
- }
-
- @Test
- public void onViewAppeared() {
- child1.onViewWillAppear();
- verify(child1, times(0)).onViewBroughtToFront();
- assertThat(uut.size()).isOne();
- }
-
- @Test
- public void onViewDisappear() {
- child1.onViewWillAppear();
- child2.onViewWillAppear();
- assertThat(uut.size()).isEqualTo(2);
- child2.onViewDisappear();
- verify(child1, times(1)).onViewBroughtToFront();
- assertThat(uut.size()).isOne();
- }
-
- @Test
- public void onChildDestroyed() {
- child1.destroy();
- assertThat(uut.size()).isEqualTo(0);
- }
-
- @Test
- public void onViewDisappear_doesNotCrashIfNoViewsHaveAppeared() {
- uut.onViewDisappear(child1);
- }
-}
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 0f09278b197..4846fa48d49 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
@@ -30,7 +30,7 @@ import kotlin.test.assertFalse
private const val UUT_WIDTH = 1000
private const val UUT_HEIGHT = 100
-@Ignore("New architecture - WIP")
+@Ignore("New architecture - failed to fix")
class TitleAndButtonsContainerTest : BaseTest() {
lateinit var uut: TitleAndButtonsContainer
private lateinit var activity: Activity
diff --git a/package-lock.json b/package-lock.json
index 9c4d21a603e..0efff00ef6e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15486,7 +15486,7 @@
},
"node_modules/npm/node_modules/lodash._baseindexof": {
"version": "3.1.0",
- "dev": true,
+ "extraneous": true,
"inBundle": true,
"license": "MIT"
},
@@ -15502,19 +15502,19 @@
},
"node_modules/npm/node_modules/lodash._bindcallback": {
"version": "3.0.1",
- "dev": true,
+ "extraneous": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/lodash._cacheindexof": {
"version": "3.0.2",
- "dev": true,
+ "extraneous": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/lodash._createcache": {
"version": "3.1.2",
- "dev": true,
+ "extraneous": true,
"inBundle": true,
"license": "MIT",
"dependencies": {
@@ -15529,7 +15529,7 @@
},
"node_modules/npm/node_modules/lodash._getnative": {
"version": "3.9.1",
- "dev": true,
+ "extraneous": true,
"inBundle": true,
"license": "MIT"
},
@@ -15547,7 +15547,7 @@
},
"node_modules/npm/node_modules/lodash.restparam": {
"version": "3.6.1",
- "dev": true,
+ "extraneous": true,
"inBundle": true,
"license": "MIT"
},
diff --git a/playground/android/gradle.properties b/playground/android/gradle.properties
index 33663d6115c..5b8b5878d26 100644
--- a/playground/android/gradle.properties
+++ b/playground/android/gradle.properties
@@ -22,4 +22,6 @@ android.useAndroidX=true
android.jetifier.ignorelist=bcprov
hermesEnabled=true
newArchEnabled=true
-FLIPPER_VERSION=0.191.0
\ No newline at end of file
+FLIPPER_VERSION=0.191.0
+
+android.experimental.androidTest.numManagedDeviceShards=3
diff --git a/scripts/test-unit.js b/scripts/test-unit.js
index 214728ada25..50a097fb87d 100644
--- a/scripts/test-unit.js
+++ b/scripts/test-unit.js
@@ -20,6 +20,7 @@ function runAndroidUnitTests() {
// exec.execSync(`echo y | ${sdkmanager} --update && echo y | ${sdkmanager} --licenses`);
}
exec.execSync(`cd playground/android && ./gradlew ${conf}`);
+ exec.execSync(`cd playground/android && ./gradlew :react-native-navigation:pixel3aapi34DebugAndroidTest`);
}
function runIosUnitTests() {