Add instrumentation tests for list items
Fixes: 158782293
Fixes: 149053988
Test: atest CarUILibUnitTests
Change-Id: Id7fc3603bccb32ab68ea59b037d1c91d74c20cf0
diff --git a/car-ui-lib/tests/unit/AndroidManifest.xml b/car-ui-lib/tests/unit/AndroidManifest.xml
index 4848fca..e499db5 100644
--- a/car-ui-lib/tests/unit/AndroidManifest.xml
+++ b/car-ui-lib/tests/unit/AndroidManifest.xml
@@ -19,6 +19,7 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<activity android:name="com.android.car.ui.TestActivity" />
+ <activity android:name="com.android.car.ui.recyclerview.CarUiRecyclerViewTestActivity" />
<activity android:name="com.android.car.ui.FocusAreaTestActivity" />
<activity android:name="com.android.car.ui.FocusParkingViewTestActivity" />
<activity
diff --git a/car-ui-lib/tests/unit/src/com/android/car/ui/recyclerview/CarUiListItemTest.java b/car-ui-lib/tests/unit/src/com/android/car/ui/recyclerview/CarUiListItemTest.java
new file mode 100644
index 0000000..d5f5d13
--- /dev/null
+++ b/car-ui-lib/tests/unit/src/com/android/car/ui/recyclerview/CarUiListItemTest.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2020 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.recyclerview;
+
+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.isChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.view.View;
+
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.car.ui.tests.unit.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Unit tests for {@link CarUiListItem}. */
+public class CarUiListItemTest {
+
+ private CarUiRecyclerView mCarUiRecyclerView;
+
+ @Rule
+ public ActivityTestRule<CarUiRecyclerViewTestActivity> mActivityRule =
+ new ActivityTestRule<>(CarUiRecyclerViewTestActivity.class);
+
+ @Before
+ public void setUp() {
+ mCarUiRecyclerView = mActivityRule.getActivity().requireViewById(R.id.list);
+ }
+
+ @Test
+ public void testItemVisibility_withTitle() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+ item.setTitle("Test title");
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.title)).check(matches(isDisplayed()));
+ onView(withId(R.id.body)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.icon_container)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.action_container)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testItemVisibility_withBody() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+ item.setBody("Test body");
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.body)).check(matches(isDisplayed()));
+ onView(withId(R.id.title)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.icon_container)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.action_container)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testItemVisibility_withTitle_withBodyAndIcon() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+ item.setTitle("Test title");
+ item.setBody("Test body");
+ item.setIcon(mActivityRule.getActivity().getDrawable(R.drawable.car_ui_icon_close));
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.title)).check(matches(isDisplayed()));
+ onView(withId(R.id.body)).check(matches(isDisplayed()));
+ onView(withId(R.id.icon_container)).check(matches(isDisplayed()));
+ onView(withId(R.id.action_container)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testItem_withCheckbox() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem.OnCheckedChangeListener mockOnCheckedChangeListener = mock(
+ CarUiContentListItem.OnCheckedChangeListener.class);
+
+ CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.CHECK_BOX);
+ item.setTitle("Test item with checkbox");
+ item.setOnCheckedChangeListener(mockOnCheckedChangeListener);
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.title)).check(matches(isDisplayed()));
+ onView(withId(R.id.checkbox_widget)).check(matches(isDisplayed()));
+ onView(withId(R.id.action_divider)).check(matches(not(isDisplayed())));
+
+ // List item with checkbox should be initially unchecked.
+ onView(withId(R.id.checkbox_widget)).check(matches(isNotChecked()));
+ // Clicks anywhere on the item should toggle the checkbox
+ onView(withId(R.id.title)).perform(click());
+ onView(withId(R.id.checkbox_widget)).check(matches(isChecked()));
+ // Check that onCheckChangedListener was invoked.
+ verify(mockOnCheckedChangeListener, times(1)).onCheckedChanged(item, true);
+
+ // Uncheck checkbox with click on the action container
+ onView(withId(R.id.action_container)).perform(click());
+ onView(withId(R.id.checkbox_widget)).check(matches(isNotChecked()));
+ // Check that onCheckChangedListener was invoked.
+ verify(mockOnCheckedChangeListener, times(1)).onCheckedChanged(item, false);
+ }
+
+ @Test
+ public void testItem_withSwitch() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.SWITCH);
+ item.setBody("Test item with switch");
+ item.setChecked(true);
+ item.setActionDividerVisible(true);
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.body)).check(matches(isDisplayed()));
+ onView(withId(R.id.switch_widget)).check(matches(isDisplayed()));
+ onView(withId(R.id.action_divider)).check(matches(isDisplayed()));
+
+ // List item with checkbox should be initially checked.
+ onView(withId(R.id.switch_widget)).check(matches(isChecked()));
+ // Clicks anywhere on the item should toggle the switch
+ onView(withId(R.id.switch_widget)).perform(click());
+ onView(withId(R.id.switch_widget)).check(matches(isNotChecked()));
+ // Uncheck checkbox with click on the action container
+ onView(withId(R.id.body)).perform(click());
+ onView(withId(R.id.switch_widget)).check(matches(isChecked()));
+ }
+
+ @Test
+ public void testItem_withRadioButton() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem item = new CarUiContentListItem(
+ CarUiContentListItem.Action.RADIO_BUTTON);
+ item.setTitle("Test item with radio button");
+ item.setChecked(false);
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.title)).check(matches(isDisplayed()));
+ onView(withId(R.id.radio_button_widget)).check(matches(isDisplayed()));
+
+ // List item with checkbox should be initially not checked.
+ onView(withId(R.id.radio_button_widget)).check(matches(isNotChecked()));
+ // Clicks anywhere on the item should toggle the radio button.
+ onView(withId(R.id.radio_button_widget)).perform(click());
+ onView(withId(R.id.radio_button_widget)).check(matches(isChecked()));
+
+ // Repeated clicks on a selected radio button should not toggle the element once checked.
+ onView(withId(R.id.title)).perform(click());
+ onView(withId(R.id.radio_button_widget)).check(matches(isChecked()));
+ }
+
+ @Test
+ public void testItem_withListener() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem.OnClickListener mockOnCheckedChangeListener = mock(
+ CarUiContentListItem.OnClickListener.class);
+
+ CarUiContentListItem item = new CarUiContentListItem(
+ CarUiContentListItem.Action.NONE);
+ item.setIcon(mActivityRule.getActivity().getDrawable(R.drawable.car_ui_icon_close));
+ item.setTitle("Test item with listener");
+ item.setBody("Body text");
+ item.setOnItemClickedListener(mockOnCheckedChangeListener);
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.title)).check(matches(isDisplayed()));
+
+ // Clicks anywhere on the item should toggle the listener
+ onView(withId(R.id.title)).perform(click());
+ verify(mockOnCheckedChangeListener, times(1)).onClick(item);
+
+ onView(withId(R.id.body)).perform(click());
+ verify(mockOnCheckedChangeListener, times(2)).onClick(item);
+
+ onView(withId(R.id.icon_container)).perform(click());
+ verify(mockOnCheckedChangeListener, times(3)).onClick(item);
+ }
+
+ @Test
+ public void testItem_withSupplementalIconAndIconOnClickListener() {
+ List<CarUiListItem> items = new ArrayList<>();
+
+ CarUiContentListItem.OnClickListener mockedItemOnClickListener = mock(
+ CarUiContentListItem.OnClickListener.class);
+ View.OnClickListener mockedIconListener = mock(View.OnClickListener.class);
+
+ CarUiContentListItem item = new CarUiContentListItem(
+ CarUiContentListItem.Action.ICON);
+ item.setSupplementalIcon(
+ mActivityRule.getActivity().getDrawable(R.drawable.car_ui_icon_close),
+ mockedIconListener);
+ item.setOnItemClickedListener(mockedItemOnClickListener);
+ item.setTitle("Test item with listeners");
+ items.add(item);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiListItemAdapter(items)));
+
+ onView(withId(R.id.title)).check(matches(isDisplayed()));
+
+ // Clicks anywhere on the item (outside of the icon) should only invoke the item click
+ // listener.
+ onView(withId(R.id.title)).perform(click());
+ verify(mockedItemOnClickListener, times(1)).onClick(item);
+
+ // Clicks anywhere on the icon should invoke both listeners.
+ onView(withId(R.id.action_container)).perform(click());
+ verify(mockedItemOnClickListener, times(2)).onClick(item);
+ verify(mockedIconListener, times(1)).onClick(ArgumentMatchers.any(View.class));
+ }
+
+ @Test
+ public void testRadioButtonListItemAdapter() {
+ List<CarUiRadioButtonListItem> items = new ArrayList<>();
+
+ CarUiRadioButtonListItem itemOne = new CarUiRadioButtonListItem();
+ String itemOneTitle = "Item 1";
+ itemOne.setTitle(itemOneTitle);
+ items.add(itemOne);
+
+ CarUiRadioButtonListItem itemTwo = new CarUiRadioButtonListItem();
+ String itemTwoTitle = "Item 2";
+ itemTwo.setTitle(itemTwoTitle);
+ items.add(itemTwo);
+
+ CarUiRadioButtonListItem itemThree = new CarUiRadioButtonListItem();
+ String itemThreeTitle = "Item 3";
+ itemThree.setTitle(itemThreeTitle);
+ items.add(itemThree);
+
+ mCarUiRecyclerView.post(
+ () -> mCarUiRecyclerView.setAdapter(new CarUiRadioButtonListItemAdapter(items)));
+
+ onView(withText(itemOneTitle)).check(matches(isDisplayed()));
+ onView(withText(itemTwoTitle)).check(matches(isDisplayed()));
+ onView(withText(itemThreeTitle)).check(matches(isDisplayed()));
+
+ // All items are initially unchecked.
+ assertFalse(itemOne.isChecked());
+ assertFalse(itemTwo.isChecked());
+ assertFalse(itemThree.isChecked());
+
+ // Select first item.
+ onView(withText(itemOneTitle)).perform(click());
+ assertTrue(itemOne.isChecked());
+ assertFalse(itemTwo.isChecked());
+ assertFalse(itemThree.isChecked());
+
+ // Select second item.
+ onView(withText(itemTwoTitle)).perform(click());
+ assertFalse(itemOne.isChecked());
+ assertTrue(itemTwo.isChecked());
+ assertFalse(itemThree.isChecked());
+
+ // Select third item.
+ onView(withText(itemThreeTitle)).perform(click());
+ assertFalse(itemOne.isChecked());
+ assertFalse(itemTwo.isChecked());
+ assertTrue(itemThree.isChecked());
+ }
+}
diff --git a/car-ui-lib/tests/unit/src/com/android/car/ui/recyclerview/CarUiRecyclerViewTestActivity.java b/car-ui-lib/tests/unit/src/com/android/car/ui/recyclerview/CarUiRecyclerViewTestActivity.java
new file mode 100644
index 0000000..6fb5746
--- /dev/null
+++ b/car-ui-lib/tests/unit/src/com/android/car/ui/recyclerview/CarUiRecyclerViewTestActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 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.recyclerview;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.car.ui.tests.unit.R;
+
+/**
+ * An {@link Activity} that contains only an empty {@link CarUiRecyclerView}.
+ */
+public class CarUiRecyclerViewTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.car_ui_recycler_view_test_activity);
+ }
+}