diff options
Diffstat (limited to 'adservices/apk/tests/src/com')
6 files changed, 707 insertions, 576 deletions
diff --git a/adservices/apk/tests/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java b/adservices/apk/tests/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java index 31bba31bc8..cc9d5ab749 100644 --- a/adservices/apk/tests/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java +++ b/adservices/apk/tests/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java @@ -27,13 +27,20 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; -import android.content.pm.PackageManager; import androidx.core.app.NotificationManagerCompat; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiObjectNotFoundException; +import androidx.test.uiautomator.UiSelector; +import androidx.test.uiautomator.Until; import com.android.adservices.api.R; +import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.consent.ConsentManager; import com.android.dx.mockito.inline.extended.ExtendedMockito; @@ -48,6 +55,8 @@ import org.mockito.quality.Strictness; @RunWith(AndroidJUnit4.class) public class ConsentNotificationTriggerTest { private static final String NOTIFICATION_CHANNEL_ID = "PRIVACY_SANDBOX_CHANNEL"; + private static final int LAUNCH_TIMEOUT = 5000; + private static UiDevice sDevice; @Mock private NotificationManagerCompat mNotificationManagerCompat; @Mock private ConsentManager mConsentManager; @@ -59,17 +68,21 @@ public class ConsentNotificationTriggerTest { @Before public void setUp() { mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + // Initialize UiDevice instance + sDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); } @Test - public void testEuNotification() throws InterruptedException { + public void testEuNotification() throws InterruptedException, UiObjectNotFoundException { MockitoAnnotations.initMocks(this); mStaticMockSession = ExtendedMockito.mockitoSession() + .spyStatic(FlagsFactory.class) .strictness(Strictness.WARN) .initMocks(this) .startMocking(); try { + ExtendedMockito.doReturn(FlagsFactory.getFlagsForTest()).when(FlagsFactory::getFlags); mNotificationManager = mContext.getSystemService(NotificationManager.class); final String expectedTitle = mContext.getString(R.string.notificationUI_notification_title_eu); @@ -87,14 +100,32 @@ public class ConsentNotificationTriggerTest { .isEqualTo(expectedTitle); assertThat(notification.extras.getCharSequence(Notification.EXTRA_TEXT)) .isEqualTo(expectedContent); - Thread.sleep(5000); // wait 5s to make sure that Notification disappears. + + sDevice.openNotification(); + sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); + UiObject scroller = + sDevice.findObject( + new UiSelector() + .packageName("com.android.systemui") + .resourceId( + "com.android.systemui:id/notification_stack_scroller")); + assertThat(scroller.exists()).isTrue(); + UiSelector notificationCardSelector = + new UiSelector().text(getString(R.string.notificationUI_notification_title_eu)); + UiObject notificationCard = scroller.getChild(notificationCardSelector); + assertThat(notificationCard.exists()).isTrue(); + + notificationCard.click(); + Thread.sleep(LAUNCH_TIMEOUT); + UiObject title = getElement(R.string.notificationUI_header_title_eu); + assertThat(title.exists()).isTrue(); } finally { mStaticMockSession.finishMocking(); } } @Test - public void testNonEuNotifications() throws InterruptedException { + public void testNonEuNotifications() throws InterruptedException, UiObjectNotFoundException { MockitoAnnotations.initMocks(this); mStaticMockSession = ExtendedMockito.mockitoSession() @@ -114,7 +145,7 @@ public class ConsentNotificationTriggerTest { Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. verify(mConsentManager).enable(any(Context.class)); - verify(mConsentManager).recordNotificationDisplayed(any(PackageManager.class)); + verify(mConsentManager).recordNotificationDisplayed(); verifyNoMoreInteractions(mConsentManager); assertThat(mNotificationManager.getActiveNotifications()).hasLength(1); final Notification notification = @@ -124,7 +155,27 @@ public class ConsentNotificationTriggerTest { .isEqualTo(expectedTitle); assertThat(notification.extras.getCharSequence(Notification.EXTRA_TEXT)) .isEqualTo(expectedContent); - Thread.sleep(5000); // wait 5s to make sure that Notification disappears. + + sDevice.openNotification(); + sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); + + UiObject scroller = + sDevice.findObject( + new UiSelector() + .packageName("com.android.systemui") + .resourceId( + "com.android.systemui:id/notification_stack_scroller")); + assertThat(scroller.exists()).isTrue(); + UiObject notificationCard = + scroller.getChild( + new UiSelector() + .text(getString(R.string.notificationUI_notification_title))); + assertThat(notificationCard.exists()).isTrue(); + + notificationCard.click(); + Thread.sleep(LAUNCH_TIMEOUT); + UiObject title = getElement(R.string.notificationUI_header_title); + assertThat(title.exists()).isTrue(); } finally { mStaticMockSession.finishMocking(); } @@ -148,10 +199,18 @@ public class ConsentNotificationTriggerTest { ConsentNotificationTrigger.showConsentNotification(mContext, true); - verify(mConsentManager).recordNotificationDisplayed(any(PackageManager.class)); + verify(mConsentManager).recordNotificationDisplayed(); verifyNoMoreInteractions(mConsentManager); } finally { mStaticMockSession.finishMocking(); } } + + private String getString(int resourceId) { + return ApplicationProvider.getApplicationContext().getResources().getString(resourceId); + } + + private UiObject getElement(int resId) { + return sDevice.findObject(new UiSelector().text(getString(resId))); + } } diff --git a/adservices/apk/tests/src/com/android/adservices/ui/notifications/NotificationActivityTest.java b/adservices/apk/tests/src/com/android/adservices/ui/notifications/NotificationActivityTest.java deleted file mode 100644 index e02dab4b08..0000000000 --- a/adservices/apk/tests/src/com/android/adservices/ui/notifications/NotificationActivityTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.adservices.ui.notifications; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -import static org.mockito.ArgumentMatchers.any; - -import android.content.Context; -import android.content.Intent; - -import androidx.test.core.app.ActivityScenario; - -import com.android.adservices.api.R; -import com.android.adservices.service.common.BackgroundJobsManager; -import com.android.adservices.ui.settings.AdServicesSettingsActivity; -import com.android.adservices.ui.settings.fragments.AdServicesSettingsMainFragment; -import com.android.dx.mockito.inline.extended.ExtendedMockito; -import com.android.dx.mockito.inline.extended.StaticMockitoSession; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockitoAnnotations; -import org.mockito.quality.Strictness; - -/** Tests for {@link ConsentNotificationActivity}. */ -public class NotificationActivityTest { - private static final String NOTIFICATION_INTENT = "android.test.adservices.ui.NOTIFICATIONS"; - private StaticMockitoSession mStaticMockSession; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mStaticMockSession = - ExtendedMockito.mockitoSession() - .spyStatic(BackgroundJobsManager.class) - .strictness(Strictness.WARN) - .initMocks(this) - .startMocking(); - ExtendedMockito.doNothing() - .when(() -> BackgroundJobsManager.scheduleAllBackgroundJobs(any(Context.class))); - - Intent mIntent = new Intent(NOTIFICATION_INTENT); - mIntent.putExtra("isEUDevice", false); - ActivityScenario.launch(mIntent); - } - - @After - public void teardown() { - if (mStaticMockSession != null) { - mStaticMockSession.finishMocking(); - } - } - - /** - * Test if {@link AdServicesSettingsMainFragment} is displayed in {@link - * AdServicesSettingsActivity}. - */ - @Test - public void test_FragmentContainer_isDisplayed() { - onView(withId(R.id.fragment_container_view)).check(matches(isDisplayed())); - } - - /** - * Test if {@link ConsentNotificationFragment} is displayed in {@link - * ConsentNotificationActivity}. - */ - @Test - public void test_ConsentNotificationFragment_isDisplayed() { - checkConsentNotificationFragmentIsDisplayed(); - } - - @Test - public void test_ConsentNotificationConfirmationFragment_isDisplayed() { - launchEUActivity(); - checkConsentNotificationFragmentIsDisplayed(); - - onView(withId(R.id.rightControlButton)).perform(click()); - - onView(withId(R.id.consent_notification_accept_confirmation_view)) - .check(matches(isDisplayed())); - } - - private void checkConsentNotificationFragmentIsDisplayed() { - onView(withText(R.string.notificationUI_header_title)).check(matches(isDisplayed())); - } - - private void launchEUActivity() { - Intent mIntent = new Intent(NOTIFICATION_INTENT); - mIntent.putExtra("isEUDevice", true); - ActivityScenario.launch(mIntent); - } -} diff --git a/adservices/apk/tests/src/com/android/adservices/ui/notifications/NotificationActivityUiAutomatorTest.java b/adservices/apk/tests/src/com/android/adservices/ui/notifications/NotificationActivityUiAutomatorTest.java new file mode 100644 index 0000000000..cd072cc619 --- /dev/null +++ b/adservices/apk/tests/src/com/android/adservices/ui/notifications/NotificationActivityUiAutomatorTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.adservices.ui.notifications; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.content.Intent; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiObjectNotFoundException; +import androidx.test.uiautomator.UiSelector; +import androidx.test.uiautomator.Until; + +import com.android.adservices.api.R; +import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.PhFlags; +import com.android.adservices.service.common.BackgroundJobsManager; +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.io.IOException; + +@RunWith(AndroidJUnit4.class) +public class NotificationActivityUiAutomatorTest { + private static final String NOTIFICATION_TEST_PACKAGE = + "android.test.adservices.ui.NOTIFICATIONS"; + private static final int LAUNCH_TIMEOUT = 5000; + private static Context sContext; + private static UiDevice sDevice; + private static Intent sIntent; + private MockitoSession mStaticMockSession; + private PhFlags mPhFlags; + + @Before + public void setup() throws UiObjectNotFoundException, IOException { + MockitoAnnotations.initMocks(this); + mStaticMockSession = + ExtendedMockito.mockitoSession() + .spyStatic(PhFlags.class) + .spyStatic(BackgroundJobsManager.class) + .spyStatic(FlagsFactory.class) + .strictness(Strictness.WARN) + .initMocks(this) + .startMocking(); + ExtendedMockito.doReturn(FlagsFactory.getFlagsForTest()).when(FlagsFactory::getFlags); + ExtendedMockito.doNothing() + .when(() -> BackgroundJobsManager.scheduleAllBackgroundJobs(any(Context.class))); + mPhFlags = spy(PhFlags.getInstance()); + doReturn(true).when(mPhFlags).getUIDialogsFeatureEnabled(); + ExtendedMockito.doReturn(mPhFlags).when(PhFlags::getInstance); + + // Initialize UiDevice instance + sDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + // Start from the home screen + sDevice.pressHome(); + + // Wait for launcher + final String launcherPackage = sDevice.getLauncherPackageName(); + assertThat(launcherPackage).isNotNull(); + sDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); + + // Create intent + sContext = ApplicationProvider.getApplicationContext(); + sIntent = new Intent(NOTIFICATION_TEST_PACKAGE); + sIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + + @After + public void teardown() { + if (mStaticMockSession != null) { + mStaticMockSession.finishMocking(); + } + } + + @Test + public void moreButtonTest() throws UiObjectNotFoundException, InterruptedException { + startActivity(true); + UiObject leftControlButton = + getElement(R.string.notificationUI_left_control_button_text_eu); + UiObject rightControlButton = + getElement(R.string.notificationUI_right_control_button_text_eu); + UiObject moreButton = getElement(R.string.notificationUI_more_button_text); + assertThat(leftControlButton.exists()).isFalse(); + assertThat(rightControlButton.exists()).isFalse(); + assertThat(moreButton.exists()).isTrue(); + + while (moreButton.exists()) { + moreButton.click(); + Thread.sleep(2000); + } + assertThat(leftControlButton.exists()).isTrue(); + assertThat(rightControlButton.exists()).isTrue(); + assertThat(moreButton.exists()).isFalse(); + } + + @Test + public void acceptedConfirmationScreenTest() + throws UiObjectNotFoundException, InterruptedException { + startActivity(true); + UiObject leftControlButton = + getElement(R.string.notificationUI_left_control_button_text_eu); + UiObject rightControlButton = + getElement(R.string.notificationUI_right_control_button_text_eu); + UiObject moreButton = getElement(R.string.notificationUI_more_button_text); + assertThat(leftControlButton.exists()).isFalse(); + assertThat(rightControlButton.exists()).isFalse(); + assertThat(moreButton.exists()).isTrue(); + + while (moreButton.exists()) { + moreButton.click(); + Thread.sleep(2000); + } + assertThat(leftControlButton.exists()).isTrue(); + assertThat(rightControlButton.exists()).isTrue(); + assertThat(moreButton.exists()).isFalse(); + + rightControlButton.click(); + UiObject acceptedTitle = getElement(R.string.notificationUI_confirmation_accept_title); + assertThat(acceptedTitle.exists()).isTrue(); + } + + private void startActivity(boolean isEUActivity) { + // Send intent + sIntent.putExtra("isEUDevice", isEUActivity); + sContext.startActivity(sIntent); + + // Wait for the app to appear + sDevice.wait(Until.hasObject(By.pkg(NOTIFICATION_TEST_PACKAGE).depth(0)), LAUNCH_TIMEOUT); + } + + private String getString(int resourceId) { + return ApplicationProvider.getApplicationContext().getResources().getString(resourceId); + } + + private UiObject getElement(int resId) { + return sDevice.findObject(new UiSelector().text(getString(resId))); + } +} diff --git a/adservices/apk/tests/src/com/android/adservices/ui/settings/AdServicesSettingsActivityWrapper.java b/adservices/apk/tests/src/com/android/adservices/ui/settings/AdServicesSettingsActivityWrapper.java deleted file mode 100644 index c58e51e53c..0000000000 --- a/adservices/apk/tests/src/com/android/adservices/ui/settings/AdServicesSettingsActivityWrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.adservices.ui.settings; - -import com.android.adservices.service.PhFlags; -import com.android.adservices.ui.settings.viewmodels.TopicsViewModel; - - -/** - * Wrapper class for {@link AdServicesSettingsActivity} used for testing purposes only. Instantiates - * a {@link AdServicesSettingsActivity} with the custom constructor that can be passed a a mocked - * view model provider. This is needed because the some view models (such as the {@link - * TopicsViewModel}) will ultimately need to call {@link PhFlags} to read system settings, which - * requires the READ_DEVICE_CONFIG permission that is only granted to the real PP API process and - * not the process used for the test application. - */ -public class AdServicesSettingsActivityWrapper extends AdServicesSettingsActivity { - public AdServicesSettingsActivityWrapper() { - super(new SettingsActivityTest().generateMockedViewModelProvider()); - } -} diff --git a/adservices/apk/tests/src/com/android/adservices/ui/settings/SettingsActivityTest.java b/adservices/apk/tests/src/com/android/adservices/ui/settings/SettingsActivityTest.java deleted file mode 100644 index de13095766..0000000000 --- a/adservices/apk/tests/src/com/android/adservices/ui/settings/SettingsActivityTest.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.adservices.ui.settings; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.Espresso.pressBack; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; -import static androidx.test.espresso.matcher.ViewMatchers.isChecked; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; -import static androidx.test.espresso.matcher.ViewMatchers.withClassName; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; - -import android.content.Context; -import android.graphics.Rect; -import android.util.Log; -import android.view.View; -import android.widget.HorizontalScrollView; -import android.widget.ListView; -import android.widget.ScrollView; -import android.widget.Switch; - -import androidx.core.widget.NestedScrollView; -import androidx.lifecycle.ViewModelProvider; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.PerformException; -import androidx.test.espresso.UiController; -import androidx.test.espresso.ViewAction; -import androidx.test.espresso.action.ViewActions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.espresso.util.HumanReadables; -import androidx.test.ext.junit.rules.ActivityScenarioRule; - -import com.android.adservices.api.R; -import com.android.adservices.data.topics.Topic; -import com.android.adservices.service.common.BackgroundJobsManager; -import com.android.adservices.service.consent.App; -import com.android.adservices.service.consent.ConsentManager; -import com.android.adservices.ui.settings.fragments.AdServicesSettingsMainFragment; -import com.android.adservices.ui.settings.viewmodels.AppsViewModel; -import com.android.adservices.ui.settings.viewmodels.MainViewModel; -import com.android.adservices.ui.settings.viewmodels.TopicsViewModel; -import com.android.dx.mockito.inline.extended.ExtendedMockito; - -import com.google.common.collect.ImmutableList; - -import junit.framework.AssertionFailedError; - -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** Tests for {@link AdServicesSettingsActivity}. */ -public class SettingsActivityTest { - static ViewModelProvider sViewModelProvider = Mockito.mock(ViewModelProvider.class); - static ConsentManager sConsentManager; - private MockitoSession mStaticMockSession; - - private static final class NestedScrollToAction implements ViewAction { - private static final String TAG = - androidx.test.espresso.action.ScrollToAction.class.getSimpleName(); - - @Override - public Matcher<View> getConstraints() { - return Matchers.allOf( - ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE), - ViewMatchers.isDescendantOfA( - Matchers.anyOf( - ViewMatchers.isAssignableFrom(NestedScrollView.class), - ViewMatchers.isAssignableFrom(ScrollView.class), - ViewMatchers.isAssignableFrom(HorizontalScrollView.class), - ViewMatchers.isAssignableFrom(ListView.class)))); - } - - @Override - public void perform(UiController uiController, View view) { - if (isDisplayingAtLeast(90).matches(view)) { - Log.i(TAG, "View is already displayed. Returning."); - return; - } - Rect rect = new Rect(); - view.getDrawingRect(rect); - if (!view.requestRectangleOnScreen(rect, true /* immediate */)) { - Log.w(TAG, "Scrolling to view was requested, but none of the parents scrolled."); - } - uiController.loopMainThreadUntilIdle(); - if (!isDisplayingAtLeast(90).matches(view)) { - throw new PerformException.Builder() - .withActionDescription(this.getDescription()) - .withViewDescription(HumanReadables.describe(view)) - .withCause( - new RuntimeException( - "Scrolling to view was attempted, but the view is not " - + "displayed")) - .build(); - } - } - - @Override - public String getDescription() { - return "scroll to"; - } - } - - private static ViewAction nestedScrollTo() { - return ViewActions.actionWithAssertions(new NestedScrollToAction()); - } - - /** - * {@link ActivityScenarioRule} is a JUnit {@link Rule @Rule} to launch your activity under - * test. - * - * <p>Rules are interceptors which are executed for each test method and are important building - * blocks of Junit tests. - */ - @Rule - public ActivityScenarioRule mRule = - new ActivityScenarioRule<>(AdServicesSettingsActivityWrapper.class); - - /** - * This is used by {@link AdServicesSettingsActivityWrapper}. Provides a mocked {@link - * ViewModelProvider} that serves mocked view models, which use a mocked {@link ConsentManager}, - * which gives mocked data. - * - * @return the mocked {@link ViewModelProvider} - */ - public ViewModelProvider generateMockedViewModelProvider() { - sConsentManager = - spy(ConsentManager.getInstance(ApplicationProvider.getApplicationContext())); - List<Topic> tempList = new ArrayList<>(); - tempList.add(Topic.create(10001, 1, 1)); - tempList.add(Topic.create(10002, 1, 1)); - tempList.add(Topic.create(10003, 1, 1)); - ImmutableList<Topic> topicsList = ImmutableList.copyOf(tempList); - doReturn(topicsList).when(sConsentManager).getKnownTopicsWithConsent(); - - tempList = new ArrayList<>(); - tempList.add(Topic.create(10004, 1, 1)); - tempList.add(Topic.create(10005, 1, 1)); - ImmutableList<Topic> blockedTopicsList = ImmutableList.copyOf(tempList); - doReturn(blockedTopicsList).when(sConsentManager).getTopicsWithRevokedConsent(); - - List<App> appTempList = new ArrayList<>(); - appTempList.add(App.create("app1")); - appTempList.add(App.create("app2")); - ImmutableList<App> appsList = ImmutableList.copyOf(appTempList); - doReturn(appsList).when(sConsentManager).getKnownAppsWithConsent(); - - appTempList = new ArrayList<>(); - appTempList.add(App.create("app3")); - ImmutableList<App> blockedAppsList = ImmutableList.copyOf(appTempList); - doReturn(blockedAppsList).when(sConsentManager).getAppsWithRevokedConsent(); - - doNothing().when(sConsentManager).resetTopicsAndBlockedTopics(); - try { - doNothing().when(sConsentManager).resetAppsAndBlockedApps(); - } catch (IOException e) { - e.printStackTrace(); - } - doNothing().when(sConsentManager).resetMeasurement(); - - TopicsViewModel topicsViewModel = - new TopicsViewModel(ApplicationProvider.getApplicationContext(), sConsentManager); - AppsViewModel appsViewModel = - new AppsViewModel(ApplicationProvider.getApplicationContext(), sConsentManager); - MainViewModel mainViewModel = - new MainViewModel(ApplicationProvider.getApplicationContext(), sConsentManager); - doReturn(topicsViewModel).when(sViewModelProvider).get(TopicsViewModel.class); - doReturn(mainViewModel).when(sViewModelProvider).get(MainViewModel.class); - doReturn(appsViewModel).when(sViewModelProvider).get(AppsViewModel.class); - return sViewModelProvider; - } - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mStaticMockSession = - ExtendedMockito.mockitoSession() - .spyStatic(BackgroundJobsManager.class) - .strictness(Strictness.WARN) - .initMocks(this) - .startMocking(); - ExtendedMockito.doNothing() - .when(() -> BackgroundJobsManager.scheduleAllBackgroundJobs(any(Context.class))); - } - - @After - public void teardown() { - if (mStaticMockSession != null) { - mStaticMockSession.finishMocking(); - } - } - - /** - * Test if {@link AdServicesSettingsMainFragment} is displayed in {@link - * AdServicesSettingsActivity}. - */ - @Test - public void test_FragmentContainer_isDisplayed() { - giveConsentIfNeeded(); - onView(withId(R.id.fragment_container_view)).check(matches(isDisplayed())); - } - - /** - * Test if the strings (settingsUI_topics_title, settingsUI_apps_title, - * settingsUI_main_view_title) are displayed in {@link AdServicesSettingsMainFragment}. - */ - @Test - public void test_MainFragmentView_isDisplayed() { - giveConsentIfNeeded(); - onView(withText(R.string.settingsUI_privacy_sandbox_beta_switch_title)) - .perform(nestedScrollTo()) - .check(matches(isDisplayed())); - onView(withText(R.string.settingsUI_topics_title)) - .perform(nestedScrollTo()) - .check(matches(isDisplayed())); - onView(withText(R.string.settingsUI_apps_title)) - .perform(nestedScrollTo()) - .check(matches(isDisplayed())); - } - - /** Test if {@link MainViewModel} works if Activity is recreated (simulates rotate phone). */ - @Test - public void test_MainViewModel_getConsent() { - giveConsentIfNeeded(); - onView(withId(R.id.main_fragment)) - .check( - matches( - hasDescendant( - Matchers.allOf( - withClassName(Matchers.is(Switch.class.getName())), - isChecked())))); - onView(withText(R.string.settingsUI_privacy_sandbox_beta_switch_title)) - .perform(nestedScrollTo(), click()); - - mRule.getScenario().recreate(); - onView(withId(R.id.main_fragment)) - .check( - matches( - hasDescendant( - Matchers.allOf( - withClassName(Matchers.is(Switch.class.getName())), - Matchers.not(isChecked()))))); - - // Give consent back - onView(withText(R.string.settingsUI_privacy_sandbox_beta_switch_title)) - .perform(nestedScrollTo(), click()); - onView(withId(R.id.main_fragment)) - .check( - matches( - hasDescendant( - Matchers.allOf( - withClassName(Matchers.is(Switch.class.getName())), - isChecked())))); - } - - /** - * Test if the Topics button in the main fragment opens the topics fragment, and the back button - * returns to the main fragment. - */ - @Test - public void test_TopicsView() { - giveConsentIfNeeded(); - - assertMainFragmentDisplayed(); - - onView(withText(R.string.settingsUI_topics_title)).perform(nestedScrollTo(), click()); - - assertTopicsFragmentDisplayed(); - - pressBack(); - - assertMainFragmentDisplayed(); - } - - /** - * Test if the Topics button in the main fragment opens the topics fragment, and the back button - * returns to the main fragment. - */ - @Test - public void test_BlockedTopicsView() { - giveConsentIfNeeded(); - - assertMainFragmentDisplayed(); - - onView(withText(R.string.settingsUI_topics_title)).perform(nestedScrollTo(), click()); - - assertTopicsFragmentDisplayed(); - - onView(withId(R.id.blocked_topics_button)).perform(nestedScrollTo(), click()); - - assertBlockedTopicsFragmentDisplayed(); - - pressBack(); - - assertTopicsFragmentDisplayed(); - - pressBack(); - - assertMainFragmentDisplayed(); - } - - /** - * Test if the Topics button in the main fragment opens the topics fragment, and the back button - * returns to the main fragment. - */ - @Test - public void test_AppsView() { - giveConsentIfNeeded(); - - assertMainFragmentDisplayed(); - - onView(withText(R.string.settingsUI_apps_title)).perform(nestedScrollTo(), click()); - - assertAppsFragmentDisplayed(); - - pressBack(); - - assertMainFragmentDisplayed(); - } - - /** - * Test if the Topics button in the main fragment opens the topics fragment, and the back button - * returns to the main fragment. - */ - @Test - public void test_BlockedAppsView() { - giveConsentIfNeeded(); - - assertMainFragmentDisplayed(); - - onView(withText(R.string.settingsUI_apps_title)).perform(nestedScrollTo(), click()); - - assertAppsFragmentDisplayed(); - - onView(withId(R.id.blocked_apps_button)).perform(nestedScrollTo(), click()); - - assertBlockedAppsFragmentDisplayed(); - - pressBack(); - - assertAppsFragmentDisplayed(); - - pressBack(); - - assertMainFragmentDisplayed(); - } - - private void assertMainFragmentDisplayed() { - onView(withText(R.string.settingsUI_main_view_subtitle)) - .perform(nestedScrollTo()) - .check(matches(isDisplayed())); - } - - private void assertTopicsFragmentDisplayed() { - onView(withText(R.string.settingsUI_topics_view_subtitle)) - .perform(nestedScrollTo()) - .check(matches(isDisplayed())); - } - - private void assertAppsFragmentDisplayed() { - onView(withText(R.string.settingsUI_apps_view_subtitle)) - .perform(nestedScrollTo()) - .check(matches(isDisplayed())); - } - - private void assertBlockedTopicsFragmentDisplayed() { - onView(withId(R.id.blocked_topics_list)).check(matches(isDisplayed())); - } - - private void assertBlockedAppsFragmentDisplayed() { - onView(withId(R.id.blocked_apps_list)).check(matches(isDisplayed())); - } - - private void giveConsentIfNeeded() { - try { - onView(withId(R.id.main_fragment)) - .check( - matches( - hasDescendant( - Matchers.allOf( - withClassName( - Matchers.is(Switch.class.getName())), - isChecked())))); - } catch (AssertionFailedError e) { - // Give consent - onView(withText(R.string.settingsUI_privacy_sandbox_beta_switch_title)) - .perform(nestedScrollTo(), click()); - } - } -} diff --git a/adservices/apk/tests/src/com/android/adservices/ui/settings/SettingsActivityUiAutomatorTest.java b/adservices/apk/tests/src/com/android/adservices/ui/settings/SettingsActivityUiAutomatorTest.java new file mode 100644 index 0000000000..65184b5eb2 --- /dev/null +++ b/adservices/apk/tests/src/com/android/adservices/ui/settings/SettingsActivityUiAutomatorTest.java @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.adservices.ui.settings; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.Intent; + +import androidx.lifecycle.ViewModelProvider; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiObjectNotFoundException; +import androidx.test.uiautomator.UiScrollable; +import androidx.test.uiautomator.UiSelector; +import androidx.test.uiautomator.Until; + +import com.android.adservices.api.R; +import com.android.adservices.data.topics.Topic; +import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.PhFlags; +import com.android.adservices.service.common.BackgroundJobsManager; +import com.android.adservices.service.consent.App; +import com.android.adservices.service.consent.ConsentManager; +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import com.google.common.collect.ImmutableList; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class SettingsActivityUiAutomatorTest { + private static final String PRIVACY_SANDBOX_TEST_PACKAGE = "android.test.adservices.ui.MAIN"; + private static final int LAUNCH_TIMEOUT = 5000; + private static UiDevice sDevice; + static ViewModelProvider sViewModelProvider = Mockito.mock(ViewModelProvider.class); + static ConsentManager sConsentManager; + private MockitoSession mStaticMockSession; + private PhFlags mPhFlags; + private ConsentManager mConsentManager; + + @Before + public void setup() throws UiObjectNotFoundException, IOException { + // Static mocking + mStaticMockSession = + ExtendedMockito.mockitoSession() + .spyStatic(PhFlags.class) + .spyStatic(FlagsFactory.class) + .spyStatic(BackgroundJobsManager.class) + .spyStatic(ConsentManager.class) + .strictness(Strictness.WARN) + .initMocks(this) + .startMocking(); + ExtendedMockito.doReturn(FlagsFactory.getFlagsForTest()).when(FlagsFactory::getFlags); + + // prepare objects used by static mocking + mConsentManager = + spy(ConsentManager.getInstance(ApplicationProvider.getApplicationContext())); + List<Topic> tempList = new ArrayList<>(); + tempList.add(Topic.create(10001, 1, 1)); + tempList.add(Topic.create(10002, 1, 1)); + tempList.add(Topic.create(10003, 1, 1)); + ImmutableList<Topic> topicsList = ImmutableList.copyOf(tempList); + doReturn(topicsList).when(mConsentManager).getKnownTopicsWithConsent(); + + tempList = new ArrayList<>(); + tempList.add(Topic.create(10004, 1, 1)); + tempList.add(Topic.create(10005, 1, 1)); + ImmutableList<Topic> blockedTopicsList = ImmutableList.copyOf(tempList); + doReturn(blockedTopicsList).when(mConsentManager).getTopicsWithRevokedConsent(); + + List<App> appTempList = new ArrayList<>(); + appTempList.add(App.create("app1")); + appTempList.add(App.create("app2")); + ImmutableList<App> appsList = ImmutableList.copyOf(appTempList); + doReturn(appsList).when(mConsentManager).getKnownAppsWithConsent(); + + appTempList = new ArrayList<>(); + appTempList.add(App.create("app3")); + ImmutableList<App> blockedAppsList = ImmutableList.copyOf(appTempList); + doReturn(blockedAppsList).when(mConsentManager).getAppsWithRevokedConsent(); + + doNothing().when(mConsentManager).resetTopicsAndBlockedTopics(); + doNothing().when(mConsentManager).resetTopics(); + doNothing().when(mConsentManager).revokeConsentForTopic(any(Topic.class)); + doNothing().when(mConsentManager).restoreConsentForTopic(any(Topic.class)); + try { + doNothing().when(mConsentManager).resetAppsAndBlockedApps(); + doNothing().when(mConsentManager).resetApps(); + doNothing().when(mConsentManager).revokeConsentForApp(any(App.class)); + doNothing().when(mConsentManager).restoreConsentForApp(any(App.class)); + } catch (IOException e) { + e.printStackTrace(); + } + doNothing().when(mConsentManager).resetMeasurement(); + + ExtendedMockito.doNothing() + .when(() -> BackgroundJobsManager.scheduleAllBackgroundJobs(any(Context.class))); + mPhFlags = spy(PhFlags.getInstance()); + doReturn(true).when(mPhFlags).getUIDialogsFeatureEnabled(); + ExtendedMockito.doReturn(mPhFlags).when(PhFlags::getInstance); + ExtendedMockito.doReturn(mConsentManager) + .when(() -> ConsentManager.getInstance(any(Context.class))); + + // Initialize UiDevice instance + sDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + // Start from the home screen + sDevice.pressHome(); + + // Wait for launcher + final String launcherPackage = sDevice.getLauncherPackageName(); + assertThat(launcherPackage).isNotNull(); + sDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); + + // launch app + Context context = ApplicationProvider.getApplicationContext(); + Intent intent = new Intent(PRIVACY_SANDBOX_TEST_PACKAGE); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + + // Wait for the app to appear + sDevice.wait( + Until.hasObject(By.pkg(PRIVACY_SANDBOX_TEST_PACKAGE).depth(0)), LAUNCH_TIMEOUT); + + // set consent to true if not + UiObject mainSwitch = + sDevice.findObject(new UiSelector().className("android.widget.Switch")); + assertThat(mainSwitch.exists()).isTrue(); + if (!mainSwitch.isChecked()) mainSwitch.click(); + assertThat(mainSwitch.isChecked()).isTrue(); + } + + @After + public void teardown() { + if (mStaticMockSession != null) { + mStaticMockSession.finishMocking(); + } + } + + private void scrollToAndClick(int resId) throws UiObjectNotFoundException { + UiScrollable scrollView = + new UiScrollable( + new UiSelector().scrollable(true).className("android.widget.ScrollView")); + UiObject element = + sDevice.findObject( + new UiSelector().childSelector(new UiSelector().text(getString(resId)))); + scrollView.scrollIntoView(element); + element.click(); + } + + private UiObject getElement(int resId) { + return sDevice.findObject(new UiSelector().text(getString(resId))); + } + + private UiObject getElement(int resId, int index) { + return sDevice.findObject(new UiSelector().text(getString(resId)).instance(index)); + } + + private String getString(int resourceId) { + return ApplicationProvider.getApplicationContext().getResources().getString(resourceId); + } + + @Test + public void optOutDialogTest() throws UiObjectNotFoundException { + UiObject mainSwitch = + sDevice.findObject(new UiSelector().className("android.widget.Switch")); + assertThat(mainSwitch.exists()).isTrue(); + + // click switch + mainSwitch.click(); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_opt_out_title); + UiObject positiveText = getElement(R.string.settingsUI_dialog_opt_out_positive_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(positiveText.exists()).isTrue(); + + // confirm + positiveText.click(); + assertThat(mainSwitch.isChecked()).isFalse(); + + // reset to opted in + mainSwitch.click(); + assertThat(mainSwitch.isChecked()).isTrue(); + + // click switch + mainSwitch.click(); + dialogTitle = getElement(R.string.settingsUI_dialog_opt_out_title); + UiObject negativeText = getElement(R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(negativeText.exists()).isTrue(); + + // cancel + negativeText.click(); + assertThat(mainSwitch.isChecked()).isTrue(); + } + + @Test + public void blockTopicDialogTest() throws UiObjectNotFoundException { + // open topics view + scrollToAndClick(R.string.settingsUI_topics_title); + UiObject blockTopicText = getElement(R.string.settingsUI_block_topic_title, 0); + assertThat(blockTopicText.exists()).isTrue(); + + // click block + blockTopicText.click(); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_block_topic_message); + UiObject positiveText = getElement(R.string.settingsUI_dialog_block_topic_positive_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(positiveText.exists()).isTrue(); + + // confirm + positiveText.click(); + verify(mConsentManager).revokeConsentForTopic(any(Topic.class)); + blockTopicText = getElement(R.string.settingsUI_block_topic_title, 0); + assertThat(blockTopicText.exists()).isTrue(); + + // click block again + blockTopicText.click(); + dialogTitle = getElement(R.string.settingsUI_dialog_block_topic_message); + UiObject negativeText = getElement(R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(negativeText.exists()).isTrue(); + + // cancel and verify it has still only been called once + negativeText.click(); + verify(mConsentManager).revokeConsentForTopic(any(Topic.class)); + } + + @Test + public void unblockTopicDialogTest() throws UiObjectNotFoundException { + // open topics view + scrollToAndClick(R.string.settingsUI_topics_title); + + // open blocked topics view + scrollToAndClick(R.string.settingsUI_blocked_topics_title); + UiObject unblockTopicText = getElement(R.string.settingsUI_unblock_topic_title, 0); + assertThat(unblockTopicText.exists()).isTrue(); + + // click unblock + unblockTopicText.click(); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_unblock_topic_message); + UiObject positiveText = getElement(R.string.settingsUI_dialog_unblock_topic_positive_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(positiveText.exists()).isTrue(); + + // confirm + positiveText.click(); + verify(mConsentManager).restoreConsentForTopic(any(Topic.class)); + unblockTopicText = getElement(R.string.settingsUI_unblock_topic_title, 0); + assertThat(unblockTopicText.exists()).isTrue(); + } + + @Test + public void resetTopicDialogTest() throws UiObjectNotFoundException { + // open topics view + scrollToAndClick(R.string.settingsUI_topics_title); + + // click reset + scrollToAndClick(R.string.settingsUI_reset_topics_title); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_reset_topic_message); + UiObject positiveText = getElement(R.string.settingsUI_dialog_reset_topic_positive_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(positiveText.exists()).isTrue(); + + // confirm + positiveText.click(); + verify(mConsentManager).resetTopics(); + + // click reset again + scrollToAndClick(R.string.settingsUI_reset_topics_title); + dialogTitle = getElement(R.string.settingsUI_dialog_reset_topic_message); + UiObject negativeText = getElement(R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(negativeText.exists()).isTrue(); + + // cancel and verify it has still only been called once + negativeText.click(); + verify(mConsentManager).resetTopics(); + } + + @Test + public void blockAppDialogTest() throws UiObjectNotFoundException, IOException { + // open apps view + scrollToAndClick(R.string.settingsUI_apps_title); + UiObject blockAppText = getElement(R.string.settingsUI_block_app_title, 0); + assertThat(blockAppText.exists()).isTrue(); + + // click block + blockAppText.click(); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_block_app_message); + UiObject positiveText = getElement(R.string.settingsUI_dialog_block_app_positive_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(positiveText.exists()).isTrue(); + + // confirm + positiveText.click(); + verify(mConsentManager).revokeConsentForApp(any(App.class)); + blockAppText = getElement(R.string.settingsUI_block_app_title, 0); + assertThat(blockAppText.exists()).isTrue(); + + // click block again + blockAppText.click(); + dialogTitle = getElement(R.string.settingsUI_dialog_block_app_message); + UiObject negativeText = getElement(R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(negativeText.exists()).isTrue(); + + // cancel and verify it has still only been called once + negativeText.click(); + verify(mConsentManager).revokeConsentForApp(any(App.class)); + } + + @Test + public void unblockAppDialogTest() throws UiObjectNotFoundException, IOException { + // open apps view + scrollToAndClick(R.string.settingsUI_apps_title); + + // open blocked apps view + scrollToAndClick(R.string.settingsUI_blocked_apps_title); + UiObject unblockAppText = getElement(R.string.settingsUI_unblock_app_title, 0); + assertThat(unblockAppText.exists()).isTrue(); + + // click unblock + unblockAppText.click(); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_unblock_app_message); + UiObject positiveText = getElement(R.string.settingsUI_dialog_unblock_app_positive_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(positiveText.exists()).isTrue(); + + // confirm + positiveText.click(); + verify(mConsentManager).restoreConsentForApp(any(App.class)); + unblockAppText = getElement(R.string.settingsUI_unblock_app_title, 0); + assertThat(unblockAppText.exists()).isTrue(); + } + + @Test + public void resetAppDialogTest() throws UiObjectNotFoundException, IOException { + // open apps view + scrollToAndClick(R.string.settingsUI_apps_title); + + // click reset + scrollToAndClick(R.string.settingsUI_reset_apps_title); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_reset_app_message); + UiObject positiveText = getElement(R.string.settingsUI_dialog_reset_app_positive_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(positiveText.exists()).isTrue(); + + // confirm + positiveText.click(); + verify(mConsentManager).resetApps(); + + // click reset again + scrollToAndClick(R.string.settingsUI_reset_apps_title); + dialogTitle = getElement(R.string.settingsUI_dialog_reset_app_message); + UiObject negativeText = getElement(R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle.exists()).isTrue(); + assertThat(negativeText.exists()).isTrue(); + + // cancel and verify it has still only been called once + negativeText.click(); + verify(mConsentManager).resetApps(); + } + + @Test + public void disableDialogFeatureTest() throws UiObjectNotFoundException { + doReturn(false).when(mPhFlags).getUIDialogsFeatureEnabled(); + UiObject mainSwitch = + sDevice.findObject(new UiSelector().className("android.widget.Switch")); + assertThat(mainSwitch.exists()).isTrue(); + + // click switch + mainSwitch.click(); + UiObject dialogTitle = getElement(R.string.settingsUI_dialog_opt_out_title); + assertThat(dialogTitle.exists()).isFalse(); + assertThat(mainSwitch.isChecked()).isFalse(); + + // click switch again + mainSwitch.click(); + assertThat(mainSwitch.isChecked()).isTrue(); + + // open topics view + scrollToAndClick(R.string.settingsUI_topics_title); + UiObject blockTopicText = getElement(R.string.settingsUI_block_topic_title, 0); + assertThat(blockTopicText.exists()).isTrue(); + + // block topic + blockTopicText.click(); + dialogTitle = getElement(R.string.settingsUI_dialog_block_topic_message); + assertThat(dialogTitle.exists()).isFalse(); + verify(mConsentManager).revokeConsentForTopic(any(Topic.class)); + + // reset topic + scrollToAndClick(R.string.settingsUI_reset_topics_title); + dialogTitle = getElement(R.string.settingsUI_dialog_reset_topic_message); + assertThat(dialogTitle.exists()).isFalse(); + verify(mConsentManager).resetTopics(); + + // open unblock topic view + scrollToAndClick(R.string.settingsUI_blocked_topics_title); + UiObject unblockTopicText = getElement(R.string.settingsUI_unblock_topic_title, 0); + assertThat(unblockTopicText.exists()).isTrue(); + + // click unblock + unblockTopicText.click(); + dialogTitle = getElement(R.string.settingsUI_dialog_unblock_topic_message); + assertThat(dialogTitle.exists()).isFalse(); + verify(mConsentManager).restoreConsentForTopic(any(Topic.class)); + } + + /** + * Test for the Button to show blocked topics when the list of Topics is Empty The Button should + * be disabled if blocked topics is empty + * + * @throws UiObjectNotFoundException + */ + @Test + public void blockedTopicsWhenEmptyStateButtonTest() throws UiObjectNotFoundException { + // Return an empty topics list + doReturn(ImmutableList.of()).when(mConsentManager).getKnownTopicsWithConsent(); + // Return a non-empty blocked topics list + List<Topic> tempList = new ArrayList<>(); + tempList.add(Topic.create(10004, 1, 1)); + tempList.add(Topic.create(10005, 1, 1)); + ImmutableList<Topic> blockedTopicsList = ImmutableList.copyOf(tempList); + doReturn(blockedTopicsList).when(mConsentManager).getTopicsWithRevokedConsent(); + // navigate to topics page + scrollToAndClick(R.string.settingsUI_topics_title); + UiObject blockedTopicsWhenEmptyStateButton = + sDevice.findObject( + new UiSelector() + .className("android.widget.Button") + .text(getString(R.string.settingsUI_blocked_topics_title))); + + assertThat(blockedTopicsWhenEmptyStateButton.isEnabled()).isTrue(); + } +} |