blob: f91ed899cc99c53d24c4867723d61ef073e4ae3c [file] [log] [blame]
/*
* Copyright (C) 2021 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.car.ui.toolbar;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.car.ui.actions.ViewActions.waitForView;
import static com.android.car.ui.matchers.ViewMatchers.doesNotExistOrIsNotDisplayed;
import static com.google.common.truth.Truth.assertThat;
import static org.hamcrest.core.IsNot.not;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import android.view.View;
import android.widget.Switch;
import androidx.annotation.DrawableRes;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.car.ui.core.CarUi;
import com.android.car.ui.matchers.ViewMatchers;
import com.android.car.ui.sharedlibrarysupport.SharedLibraryFactorySingleton;
import com.android.car.ui.test.R;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@SuppressWarnings("AndroidJdkLibsChecker")
@RunWith(Parameterized.class)
public class ToolbarMenuItemsTest {
@Parameterized.Parameters
public static Object[] data() {
// It's important to do no shared library first, so that the shared library will
// still be enabled when this test finishes
return new Object[] { false, true };
}
private final boolean mSharedLibEnabled;
public ToolbarMenuItemsTest(boolean sharedLibEnabled) {
mSharedLibEnabled = sharedLibEnabled;
SharedLibraryFactorySingleton.setSharedLibEnabled(sharedLibEnabled);
}
@Rule
public final ActivityScenarioRule<ToolbarTestActivity> mScenarioRule =
new ActivityScenarioRule<>(ToolbarTestActivity.class);
@Test
public void menuItems_findMenuItemById_shouldWork() {
MenuItem[] menuItem = new MenuItem[] { null };
MenuItem[] foundMenuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Button!")
.setId(5)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
foundMenuItem[0] = toolbar.requireMenuItemById(5);
});
assertThat(foundMenuItem[0]).isSameInstanceAs(menuItem[0]);
}
@Test
public void menuItems_text_shouldShow() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Button!")
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
waitForViewWithText("Button!");
onView(withText("Button!")).perform(click());
verify(callback).onClick(menuItem[0]);
}
@Test
public void menuItems_icon_shouldShow() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("test_menuitem")
.setIcon(R.drawable.ic_add)
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
assertThat(menuItem[0].isVisible()).isTrue();
assertThat(menuItem[0].isTinted()).isTrue();
assertThat(menuItem[0].isActivatable()).isFalse();
assertThat(menuItem[0].isCheckable()).isFalse();
assertThat(menuItem[0].getUxRestrictions()).isEqualTo(
CarUxRestrictions.UX_RESTRICTIONS_BASELINE);
onView(isRoot()).perform(waitForView(withContentDescription("test_menuitem"), 500));
onView(withText("test_menuitem")).check(doesNotExistOrIsNotDisplayed());
onView(withContentDescription("test_menuitem")).perform(click());
verify(callback).onClick(menuItem[0]);
}
@Test
public void menuItems_textAndIcon_shouldShow() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Test!")
.setIcon(R.drawable.ic_add)
.setShowIconAndTitle(true)
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
onView(isRoot()).perform(waitForView(withText("Test!"), 500));
onView(withText("Test!")).perform(click());
verify(callback).onClick(menuItem[0]);
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0].setTitle(R.string.test_string_test_title);
menuItem[0].setIcon(R.drawable.ic_launcher);
});
onView(withText("Test!")).check(doesNotExistOrIsNotDisplayed());
onView(withText("Test title!")).check(matches(isDisplayed()));
}
@Test
public void menuItems_removeIcon_shouldShowText() {
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Test!")
.setIcon(R.drawable.ic_add)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
onView(isRoot()).perform(waitForView(withContentDescription("Test!"), 500));
onView(withContentDescription("Test!")).check(matches(isDisplayed()));
onView(withText("Test!")).check(doesNotExistOrIsNotDisplayed());
runWithActivityAndToolbar((activity, toolbar) -> menuItem[0].setIcon(0));
onView(withText("Test!")).check(matches(isDisplayed()));
}
@Test
public void menuItems_switch_shouldShow() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setCheckable()
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
assertThat(menuItem[0].isCheckable()).isTrue();
assertThat(menuItem[0].isChecked()).isFalse();
onView(isRoot()).perform(waitForView(isSwitch(), 500));
onView(isSwitch()).perform(click());
verify(callback).onClick(menuItem[0]);
onView(isSwitch()).check(matches(isChecked()));
assertThat(menuItem[0].isCheckable()).isTrue();
assertThat(menuItem[0].isChecked()).isTrue();
}
@Test
public void menuItems_overflowText_shouldWork() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Test title!")
.setDisplayBehavior(MenuItem.DisplayBehavior.NEVER)
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
// Open overflow menu, then click on the MenuItem
onView(isRoot()).perform(waitForView(withContentDescription("Overflow")));
onView(withContentDescription("Overflow")).perform(click());
onView(withText("Test title!")).perform(click());
verify(callback).onClick(menuItem[0]);
// TODO(b/188925810): this currently isn't supported in the referencedesign shared library.
if (!mSharedLibEnabled) {
// Open overflow menu, change MenuItem's title, then click on the MenuItem
onView(withContentDescription("Overflow")).perform(click());
runWithToolbar(toolbar -> menuItem[0].setTitle("Test title 2!"));
onView(withText("Test title 2!")).perform(click());
verify(callback, times(2)).onClick(menuItem[0]);
}
}
@Test
public void menuItems_overflowIconAndText_shouldWork() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Test title!")
.setIcon(R.drawable.ic_add)
.setDisplayBehavior(MenuItem.DisplayBehavior.NEVER)
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
onView(isRoot()).perform(waitForView(withContentDescription("Overflow")));
onView(withContentDescription("Overflow")).perform(click());
onView(withText("Test title!")).perform(click());
verify(callback).onClick(menuItem[0]);
}
@Test
public void menuItems_getMenuItems_returnsSameMenuItems() {
List<MenuItem> menuItems = new ArrayList<>();
runWithActivityAndToolbar((activity, toolbar) -> {
menuItems.add(MenuItem.builder(activity)
.setTitle("Menu Item 1")
.build());
menuItems.add(MenuItem.builder(activity)
.setTitle("Menu Item 2")
.build());
toolbar.setMenuItems(menuItems);
});
onView(isRoot()).perform(waitForView(withText("Menu Item 1")));
boolean[] equal = new boolean[] { false };
runWithActivityAndToolbar((activity, toolbar) ->
equal[0] = menuItems.equals(toolbar.getMenuItems()));
assertThat(equal[0]).isTrue();
}
@Test
public void menuItems_null_shouldRemoveExistingMenuItems() {
runWithActivityAndToolbar((activity, toolbar) ->
toolbar.setMenuItems(Arrays.asList(
MenuItem.builder(activity)
.setTitle("Button!")
.build(),
MenuItem.builder(activity)
.setTitle("Button2!")
.build()
)));
waitForViewWithText("Button!");
waitForViewWithText("Button2!");
onView(withText("Button!")).check(matches(isDisplayed()));
onView(withText("Button2!")).check(matches(isDisplayed()));
runWithToolbar((toolbar) -> toolbar.setMenuItems(null));
onView(withText("Button!")).check(doesNotExist());
onView(withText("Button2!")).check(doesNotExist());
}
@Test
public void menuItems_setVisibility_shouldHide() {
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Button!")
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
waitForViewWithText("Button!");
onView(withText("Button!")).check(matches(isDisplayed()));
runWithToolbar((toolbar) -> menuItem[0].setVisible(false));
onView(withText("Button!")).check(matches(not(isDisplayed())));
}
@Test
public void menuItems_setEnabled_shouldDisable() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Test!")
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
onView(isRoot()).perform(waitForView(withText("Test!"), 500));
onView(withText("Test!")).perform(click());
verify(callback, times(1)).onClick(menuItem[0]);
runWithActivityAndToolbar((activity, toolbar) -> menuItem[0].setEnabled(false));
onView(withText("Test!")).perform(click());
verify(callback, times(1)).onClick(menuItem[0]);
}
@Test
public void menuItems_activatable_test() {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Test!")
.setIcon(R.drawable.ic_add)
.setActivatable()
.setOnClickListener(callback)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
onView(isRoot()).perform(waitForView(withContentDescription("Test!"), 500));
assertThat(menuItem[0].isActivated()).isFalse();
runWithActivityAndToolbar((activity, toolbar) -> menuItem[0].setActivated(true));
assertThat(menuItem[0].isActivated()).isTrue();
}
@Test
public void menuItems_changeOnClickListener_shouldCallNewListener() {
MenuItem.OnClickListener callback1 = mock(MenuItem.OnClickListener.class);
MenuItem.OnClickListener callback2 = mock(MenuItem.OnClickListener.class);
MenuItem[] menuItem = new MenuItem[] { null };
runWithActivityAndToolbar((activity, toolbar) -> {
menuItem[0] = MenuItem.builder(activity)
.setTitle("Button!")
.setOnClickListener(callback1)
.build();
toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
});
waitForViewWithText("Button!");
onView(withText("Button!")).perform(click());
verify(callback1, times(1)).onClick(menuItem[0]);
verify(callback2, times(0)).onClick(menuItem[0]);
runWithActivityAndToolbar((activity, toolbar) -> menuItem[0].setOnClickListener(callback2));
onView(withText("Button!")).perform(click());
verify(callback1, times(1)).onClick(menuItem[0]);
verify(callback2, times(1)).onClick(menuItem[0]);
}
@Test
public void menuItems_searchScreen_shouldHideMenuItems() {
runWithActivityAndToolbar((activity, toolbar) -> {
toolbar.setMenuItems(Arrays.asList(
MenuItem.builder(activity)
.setToSearch()
.build(),
MenuItem.builder(activity)
.setTitle("Button!")
.build()));
toolbar.setShowMenuItemsWhileSearching(true);
toolbar.setState(Toolbar.State.SEARCH);
});
waitForViewWithText("Button!");
// Even if not hiding MenuItems while searching, the search MenuItem should still be hidden
onView(withText("Button!")).check(matches(isDisplayed()));
onView(withContentDescription(R.string.car_ui_toolbar_menu_item_search_title))
.check(doesNotExistOrIsNotDisplayed());
runWithToolbar((toolbar) -> toolbar.setShowMenuItemsWhileSearching(false));
// All menuitems should be hidden if we're hiding menuitems while searching
onView(withText("Button!")).check(doesNotExistOrIsNotDisplayed());
onView(withContentDescription(R.string.car_ui_toolbar_menu_item_search_title))
.check(doesNotExistOrIsNotDisplayed());
}
private void runWithToolbar(Consumer<ToolbarController> toRun) {
mScenarioRule.getScenario().onActivity(activity -> {
ToolbarController toolbar = CarUi.requireToolbar(activity);
toRun.accept(toolbar);
});
}
private void runWithActivityAndToolbar(
BiConsumer<ToolbarTestActivity, ToolbarController> toRun) {
mScenarioRule.getScenario().onActivity(activity -> {
ToolbarController toolbar = CarUi.requireToolbar(activity);
toRun.accept(activity, toolbar);
});
}
private void waitForViewWithText(String text) {
onView(isRoot()).perform(waitForView(withText(text), 500));
}
private Matcher<View> withDrawable(@DrawableRes int drawable) {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
return ViewMatchers.withDrawable(context, com.android.car.ui.test.R.drawable.ic_launcher);
}
private Matcher<View> isSwitch() {
return Matchers.allOf(isDisplayed(), isAssignableFrom(Switch.class));
}
}