Home | History | Annotate | Download | only in fragment
      1 /*
      2  * Copyright 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.navigation.fragment;
     18 
     19 import static org.hamcrest.CoreMatchers.is;
     20 import static org.hamcrest.CoreMatchers.notNullValue;
     21 import static org.hamcrest.MatcherAssert.assertThat;
     22 
     23 import android.app.Instrumentation;
     24 import android.content.ComponentName;
     25 import android.content.Intent;
     26 import android.net.Uri;
     27 import android.os.Bundle;
     28 import android.support.annotation.IdRes;
     29 import android.support.annotation.NavigationRes;
     30 import android.support.test.InstrumentationRegistry;
     31 import android.support.test.filters.SmallTest;
     32 import android.support.test.rule.ActivityTestRule;
     33 import android.support.v4.app.TaskStackBuilder;
     34 
     35 import androidx.navigation.NavController;
     36 import androidx.navigation.NavDeepLinkBuilder;
     37 import androidx.navigation.fragment.test.BaseNavigationActivity;
     38 import androidx.navigation.fragment.test.R;
     39 import androidx.navigation.testing.TestNavigator;
     40 
     41 import org.junit.Before;
     42 import org.junit.Rule;
     43 import org.junit.Test;
     44 
     45 @SmallTest
     46 public abstract class BaseNavControllerTest<A extends BaseNavigationActivity> {
     47     private static final String TEST_ARG = "test";
     48     private static final String TEST_ARG_VALUE = "value";
     49     private static final String TEST_DEEP_LINK_ACTION = "deep_link";
     50 
     51     /**
     52      * @return The concrete Activity class under test
     53      */
     54     protected abstract Class<A> getActivityClass();
     55 
     56     @Rule
     57     public ActivityTestRule<A> mActivityRule =
     58             new ActivityTestRule<>(getActivityClass(), false, false);
     59 
     60     private Instrumentation mInstrumentation;
     61 
     62     @Before
     63     public void getInstrumentation() {
     64         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     65     }
     66 
     67     @Test
     68     public void testStartDestinationDeeplink() throws Throwable {
     69         assertDeeplink(R.id.start_test, 1);
     70     }
     71 
     72     @Test
     73     public void testDeeplink() throws Throwable {
     74         assertDeeplink(R.id.deep_link_test, 2);
     75     }
     76 
     77     @Test
     78     public void testNestedStartDestinationDeeplink() throws Throwable {
     79         assertDeeplink(R.id.nested_start_test, 2);
     80     }
     81 
     82     @Test
     83     public void testNestedDeeplink() throws Throwable {
     84         assertDeeplink(R.id.nested_deep_link_test, 3);
     85     }
     86 
     87     @Test
     88     public void testDoubleNestedStartDestinationDeeplink() throws Throwable {
     89         assertDeeplink(R.id.double_nested_start_test, 2);
     90     }
     91 
     92     @Test
     93     public void testDoubleNestedDeeplink() throws Throwable {
     94         assertDeeplink(R.id.double_nested_deep_link_test, 3);
     95     }
     96 
     97     private void assertDeeplink(@IdRes int destId, int expectedStackSize) throws Throwable {
     98         BaseNavigationActivity activity = launchDeepLink(R.navigation.nav_deep_link,
     99                 destId, null);
    100         NavController navController = activity.getNavController();
    101 
    102         assertThat(navController.getCurrentDestination().getId(), is(destId));
    103         TestNavigator navigator = navController.getNavigatorProvider()
    104                 .getNavigator(TestNavigator.class);
    105         assertThat(navigator.mBackStack.size(), is(expectedStackSize));
    106 
    107         // Test that the deep link Intent was passed through even though we don't pass in any args
    108         //noinspection ConstantConditions
    109         Intent deepLinkIntent = navigator.mBackStack.peekLast().second
    110                 .getParcelable(NavController.KEY_DEEP_LINK_INTENT);
    111         assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
    112         assertThat(deepLinkIntent.getAction(), is(TEST_DEEP_LINK_ACTION));
    113     }
    114 
    115     @Test
    116     public void testStartDestinationDeeplinkWithArgs() throws Throwable {
    117         assertDeepLinkWithArgs(R.id.start_test, 1);
    118     }
    119 
    120     @Test
    121     public void testDeeplinkWithArgs() throws Throwable {
    122         assertDeepLinkWithArgs(R.id.deep_link_test, 2);
    123     }
    124 
    125     @Test
    126     public void testNestedStartDestinationDeeplinkWithArgs() throws Throwable {
    127         assertDeepLinkWithArgs(R.id.nested_start_test, 2);
    128     }
    129 
    130     @Test
    131     public void testNestedDeeplinkWithArgs() throws Throwable {
    132         assertDeepLinkWithArgs(R.id.nested_deep_link_test, 3);
    133     }
    134 
    135     @Test
    136     public void testDoubleNestedStartDestinationDeeplinkWithArgs() throws Throwable {
    137         assertDeepLinkWithArgs(R.id.double_nested_start_test, 2);
    138     }
    139 
    140     @Test
    141     public void testDoubleNestedDeeplinkWithArgs() throws Throwable {
    142         assertDeepLinkWithArgs(R.id.double_nested_deep_link_test, 3);
    143     }
    144 
    145     private void assertDeepLinkWithArgs(@IdRes int destId, int expectedStackSize) throws Throwable {
    146         Bundle args = new Bundle();
    147         args.putString(TEST_ARG, TEST_ARG_VALUE);
    148         BaseNavigationActivity activity = launchDeepLink(R.navigation.nav_deep_link,
    149                 destId, args);
    150         NavController navController = activity.getNavController();
    151 
    152         assertThat(navController.getCurrentDestination().getId(), is(destId));
    153         TestNavigator navigator = navController.getNavigatorProvider()
    154                 .getNavigator(TestNavigator.class);
    155         assertThat(navigator.mBackStack.size(), is(expectedStackSize));
    156         //noinspection ConstantConditions
    157         assertThat(navigator.mBackStack.peekLast().second.getString(TEST_ARG), is(TEST_ARG_VALUE));
    158 
    159         // Test that the deep link Intent was passed in alongside our args
    160         //noinspection ConstantConditions
    161         Intent deepLinkIntent = navigator.mBackStack.peekLast().second
    162                 .getParcelable(NavController.KEY_DEEP_LINK_INTENT);
    163         assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
    164         assertThat(deepLinkIntent.getAction(), is(TEST_DEEP_LINK_ACTION));
    165     }
    166 
    167     @Test
    168     public void testStartDestinationUriDeepLink() throws Throwable {
    169         assertUriDeepLink("start",
    170                 R.id.start_test, 1);
    171     }
    172 
    173     @Test
    174     public void testUriDeepLink() throws Throwable {
    175         assertUriDeepLink("deep_link", R.id.deep_link_test, 2);
    176     }
    177 
    178     @Test
    179     public void testNestedStartDestinationUriDeepLink() throws Throwable {
    180         assertUriDeepLink("nested_start", R.id.nested_start_test, 2);
    181     }
    182 
    183     @Test
    184     public void testNestedUriDeepLink() throws Throwable {
    185         assertUriDeepLink("nested_deep_link", R.id.nested_deep_link_test, 3);
    186     }
    187 
    188     @Test
    189     public void testDoubleNestedStartDestinationUriDeepLink() throws Throwable {
    190         assertUriDeepLink("double_nested_start", R.id.double_nested_start_test, 2);
    191     }
    192 
    193     @Test
    194     public void testDoubleNestedUriDeepLink() throws Throwable {
    195         assertUriDeepLink("double_nested_deep_link", R.id.double_nested_deep_link_test, 3);
    196     }
    197 
    198     private void assertUriDeepLink(String path, @IdRes int destId, int expectedStackSize)
    199             throws Throwable {
    200         Uri deepLinkUri = Uri.parse("http://www.example.com/" + path + "/" + TEST_ARG_VALUE);
    201         Intent intent = new Intent(Intent.ACTION_VIEW, deepLinkUri)
    202                 .setComponent(new ComponentName(mInstrumentation.getContext(),
    203                         getActivityClass()))
    204                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    205         BaseNavigationActivity activity = launchActivity(intent);
    206         NavController navController = activity.getNavController();
    207         navController.setGraph(R.navigation.nav_deep_link);
    208 
    209         assertThat(navController.getCurrentDestination().getId(), is(destId));
    210         TestNavigator navigator = navController.getNavigatorProvider()
    211                 .getNavigator(TestNavigator.class);
    212         assertThat(navigator.mBackStack.size(), is(expectedStackSize));
    213         //noinspection ConstantConditions
    214         assertThat(navigator.mBackStack.peekLast().second.getString(TEST_ARG), is(TEST_ARG_VALUE));
    215 
    216         // Test that the deep link Intent was passed in alongside our args
    217         //noinspection ConstantConditions
    218         Intent deepLinkIntent = navigator.mBackStack.peekLast().second
    219                 .getParcelable(NavController.KEY_DEEP_LINK_INTENT);
    220         assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
    221         assertThat(deepLinkIntent.getData(), is(deepLinkUri));
    222     }
    223 
    224     private BaseNavigationActivity launchActivity(Intent intent) throws Throwable {
    225         BaseNavigationActivity activity = mActivityRule.launchActivity(intent);
    226         mInstrumentation.waitForIdleSync();
    227         NavController navController = activity.getNavController();
    228         assertThat(navController, is(notNullValue(NavController.class)));
    229         TestNavigator navigator = new TestNavigator();
    230         navController.getNavigatorProvider().addNavigator(navigator);
    231         return activity;
    232     }
    233 
    234     private BaseNavigationActivity launchDeepLink(@NavigationRes int graphId, @IdRes int destId,
    235             Bundle args) throws Throwable {
    236         TaskStackBuilder intents = new NavDeepLinkBuilder(mInstrumentation.getTargetContext())
    237                 .setGraph(graphId)
    238                 .setDestination(destId)
    239                 .setArguments(args)
    240                 .createTaskStackBuilder();
    241         Intent intent = intents.editIntentAt(0);
    242         intent.setAction(TEST_DEEP_LINK_ACTION);
    243 
    244         // Now launch the deeplink Intent
    245         BaseNavigationActivity deeplinkActivity = launchActivity(intent);
    246         NavController navController = deeplinkActivity.getNavController();
    247         navController.setGraph(graphId);
    248 
    249         return deeplinkActivity;
    250     }
    251 }
    252