Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lib/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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"
Expand Down Expand Up @@ -103,6 +114,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'
Expand All @@ -113,5 +125,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")

}
}
17 changes: 17 additions & 0 deletions lib/android/app/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactnativenavigation.test">

<application
android:name="com.reactnativenavigation.TestApplication">
<activity
android:name="com.reactnativenavigation.TestActivity"
android:exported="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.reactnativenavigation

abstract class BaseAndroidTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.reactnativenavigation

class TestActivity : NavigationActivity() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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<ReactPackage> {
val packages = listOf(MainReactPackage(null), NavigationPackage())
return packages
}

override val isHermesEnabled: Boolean
get() = true

override val isNewArchEnabled: Boolean
get() = true
}

override val reactHost: ReactHost
get() = getDefaultReactHost(this, reactNativeHost)

}
Original file line number Diff line number Diff line change
@@ -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 extends View> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<ImageLoader>()
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
}
}
Original file line number Diff line number Diff line change
@@ -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<SimpleViewController.SimpleView> {
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) {

}
}
}
Loading