package org.robolectric.shadows;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.robolectric.RuntimeEnvironment.application;
import static org.robolectric.Shadows.shadowOf;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.view.ViewParent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.TestRunners;
import org.robolectric.util.ReflectionHelpers;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
@RunWith(TestRunners.MultiApiSelfTest.class)
public class ShadowListViewTest {
private List<String> transcript;
private ListView listView;
private int checkedItemPosition;
private SparseBooleanArray checkedItemPositions;
private int lastCheckedPosition;
@Before
public void setUp() throws Exception {
transcript = new ArrayList<>();
listView = new ListView(RuntimeEnvironment.application);
}
@Test
public void addHeaderView_ShouldRecordHeaders() throws Exception {
View view0 = new View(RuntimeEnvironment.application);
view0.setId(0);
View view1 = new View(RuntimeEnvironment.application);
view1.setId(1);
View view2 = new View(RuntimeEnvironment.application);
view2.setId(2);
View view3 = new View(RuntimeEnvironment.application);
view3.setId(3);
listView.addHeaderView(view0);
listView.addHeaderView(view1);
listView.addHeaderView(view2, null, false);
listView.addHeaderView(view3, null, false);
listView.setAdapter(new ShadowCountingAdapter(2));
assertThat(listView.getHeaderViewsCount()).isEqualTo(4);
assertThat(shadowOf(listView).getHeaderViews().get(0)).isSameAs(view0);
assertThat(shadowOf(listView).getHeaderViews().get(1)).isSameAs(view1);
assertThat(shadowOf(listView).getHeaderViews().get(2)).isSameAs(view2);
assertThat(shadowOf(listView).getHeaderViews().get(3)).isSameAs(view3);
assertThat(listView.findViewById(0)).isNotNull();
assertThat(listView.findViewById(1)).isNotNull();
assertThat(listView.findViewById(2)).isNotNull();
assertThat(listView.findViewById(3)).isNotNull();
}
@Test
public void addHeaderView_shouldAttachTheViewToTheList() throws Exception {
View view = new View(RuntimeEnvironment.application);
view.setId(42);
listView.addHeaderView(view);
assertThat(listView.findViewById(42)).isSameAs(view);
}
@Test
public void addFooterView_ShouldRecordFooters() throws Exception {
View view0 = new View(RuntimeEnvironment.application);
View view1 = new View(RuntimeEnvironment.application);
listView.addFooterView(view0);
listView.addFooterView(view1);
listView.setAdapter(new ShadowCountingAdapter(3));
assertThat(shadowOf(listView).getFooterViews().get(0)).isSameAs(view0);
assertThat(shadowOf(listView).getFooterViews().get(1)).isSameAs(view1);
}
@Test
public void addFooterView_shouldAttachTheViewToTheList() throws Exception {
View view = new View(RuntimeEnvironment.application);
view.setId(42);
listView.addFooterView(view);
assertThat(listView.findViewById(42)).isSameAs(view);
}
@Test
public void setAdapter_shouldNotClearHeaderOrFooterViews() throws Exception {
View header = new View(RuntimeEnvironment.application);
listView.addHeaderView(header);
View footer = new View(RuntimeEnvironment.application);
listView.addFooterView(footer);
prepareListWithThreeItems();
assertThat(listView.getChildCount()).isEqualTo(5);
assertThat(listView.getChildAt(0)).isSameAs(header);
assertThat(listView.getChildAt(4)).isSameAs(footer);
}
@Test
public void testGetFooterViewsCount() throws Exception {
listView.addHeaderView(new View(RuntimeEnvironment.application));
listView.addFooterView(new View(RuntimeEnvironment.application));
listView.addFooterView(new View(RuntimeEnvironment.application));
prepareListWithThreeItems();
assertThat(listView.getFooterViewsCount()).isEqualTo(2);
}
@Test
public void smoothScrollBy_shouldBeRecorded() throws Exception {
listView.smoothScrollBy(42, 420);
assertThat(shadowOf(listView).getLastSmoothScrollByDistance()).isEqualTo(42);
assertThat(shadowOf(listView).getLastSmoothScrollByDuration()).isEqualTo(420);
}
@Test
public void testPerformItemClick_ShouldFireOnItemClickListener() throws Exception {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
transcript.add("item was clicked: " + position);
}
});
listView.performItemClick(null, 0, -1);
assertThat(transcript).containsExactly("item was clicked: 0");
}
@Test
public void testSetSelection_WhenNoItemSelectedListenerIsSet_ShouldDoNothing() throws Exception {
listView.setSelection(0);
}
@Test
public void findItemContainingText_shouldFindChildByString() throws Exception {
ShadowListView shadowListView = prepareListWithThreeItems();
View item1 = shadowListView.findItemContainingText("Item 1");
assertThat(item1).isSameAs(listView.getChildAt(1));
}
@Test
public void findItemContainingText_shouldReturnNullIfNotFound() throws Exception {
ShadowListView shadowListView = prepareListWithThreeItems();
assertThat(shadowListView.findItemContainingText("Non-existent item")).isNull();
}
@Test
public void clickItemContainingText_shouldPerformItemClickOnList() throws Exception {
ShadowListView shadowListView = prepareListWithThreeItems();
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
transcript.add("clicked on item " + position);
}
});
shadowListView.clickFirstItemContainingText("Item 1");
assertThat(transcript).containsExactly("clicked on item 1");
}
@Test
public void clickItemContainingText_shouldPerformItemClickOnList_arrayAdapter() throws Exception {
ArrayList<String> adapterFileList = new ArrayList<>();
adapterFileList.add("Item 1");
adapterFileList.add("Item 2");
adapterFileList.add("Item 3");
final ArrayAdapter<String> adapter = new ArrayAdapter<>(application, android.R.layout.simple_list_item_1, adapterFileList);
listView.setAdapter(adapter);
shadowOf(listView).populateItems();
ShadowListView shadowListView = shadowOf(listView);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
transcript.add("clicked on item " + adapter.getItem(position));
}
});
shadowListView.clickFirstItemContainingText("Item 3");
assertThat(transcript).containsExactly("clicked on item Item 3");
}
@Test(expected = IllegalArgumentException.class)
public void clickItemContainingText_shouldThrowExceptionIfNotFound() throws Exception {
ShadowListView shadowListView = prepareListWithThreeItems();
shadowListView.clickFirstItemContainingText("Non-existant item");
}
@Test(expected = UnsupportedOperationException.class)
public void removeAllViews_shouldThrowAnException() throws Exception {
listView.removeAllViews();
}
@Test(expected = UnsupportedOperationException.class)
public void removeView_shouldThrowAnException() throws Exception {
listView.removeView(new View(RuntimeEnvironment.application));
}
@Test(expected = UnsupportedOperationException.class)
public void removeViewAt_shouldThrowAnException() throws Exception {
listView.removeViewAt(0);
}
@Test
public void getPositionForView_shouldReturnThePositionInTheListForTheView() throws Exception {
prepareWithListAdapter();
View childViewOfListItem = ((ViewGroup) listView.getChildAt(1)).getChildAt(0);
assertThat(listView.getPositionForView(childViewOfListItem)).isEqualTo(1);
}
@Test
public void getPositionForView_shouldReturnInvalidPositionForViewThatIsNotFound() throws Exception {
prepareWithListAdapter();
View view = new View(RuntimeEnvironment.application);
shadowOf(view).setMyParent(ReflectionHelpers.createNullProxy(ViewParent.class)); // Android implementation requires the item have a parent
assertThat(listView.getPositionForView(view)).isEqualTo(AdapterView.INVALID_POSITION);
}
@Test
public void shouldRecordLatestCallToSmoothScrollToPostion() throws Exception {
listView.smoothScrollToPosition(10);
assertThat(shadowOf(listView).getSmoothScrolledPosition()).isEqualTo(10);
}
@Test
public void givenChoiceModeIsSingle_whenGettingCheckedItemPosition_thenReturnPosition() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_SINGLE).withAnyItemChecked();
assertThat(listView.getCheckedItemPosition()).isEqualTo(checkedItemPosition);
}
@Test
public void givenChoiceModeIsMultiple_whenGettingCheckedItemPosition_thenReturnInvalidPosition() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_MULTIPLE).withAnyItemChecked();
assertThat(listView.getCheckedItemPosition()).isEqualTo(ListView.INVALID_POSITION);
}
@Test
public void givenChoiceModeIsNone_whenGettingCheckedItemPosition_thenReturnInvalidPosition() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_NONE);
assertThat(listView.getCheckedItemPosition()).isEqualTo(ListView.INVALID_POSITION);
}
@Test
public void givenNoItemsChecked_whenGettingCheckedItemOisition_thenReturnInvalidPosition() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_SINGLE);
assertThat(listView.getCheckedItemPosition()).isEqualTo(ListView.INVALID_POSITION);
}
@Test
public void givenChoiceModeIsSingleAndAnItemIsChecked_whenSettingChoiceModeToNone_thenGetCheckedItemPositionShouldReturnInvalidPosition() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_SINGLE).withAnyItemChecked();
listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
assertThat(listView.getCheckedItemPosition()).isEqualTo(ListView.INVALID_POSITION);
}
@Test
public void givenChoiceModeIsMultipleAndMultipleItemsAreChecked_whenGettingCheckedItemPositions_thenReturnCheckedPositions() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_MULTIPLE).withAnyItemsChecked();
assertThat(listView.getCheckedItemCount()).isEqualTo(checkedItemPositions.size());
for (int i = 0; i < checkedItemPositions.size(); i++) {
assertThat(listView.getCheckedItemPositions().get(i)).isTrue();
}
}
@Test
public void givenChoiceModeIsSingleAndMultipleItemsAreChecked_whenGettingCheckedItemPositions_thenReturnOnlyTheLastCheckedPosition() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_SINGLE).withAnyItemsChecked();
assertThat(listView.getCheckedItemPositions().get(lastCheckedPosition)).isTrue();
assertThat(listView.getCheckedItemCount()).isEqualTo(1);
}
@Test
public void givenChoiceModeIsNoneAndMultipleItemsAreChecked_whenGettingCheckedItemPositions_thenReturnNull() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_NONE).withAnyItemsChecked();
assertNull(listView.getCheckedItemPositions());
}
@Test
public void givenItemIsNotCheckedAndChoiceModeIsSingle_whenPerformingItemClick_thenItemShouldBeChecked() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_SINGLE);
int positionToClick = anyListIndex();
listView.performItemClick(null, positionToClick, 0);
assertThat(listView.getCheckedItemPosition()).isEqualTo(positionToClick);
}
@Test
public void givenItemIsCheckedAndChoiceModeIsSingle_whenPerformingItemClick_thenItemShouldBeChecked() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_SINGLE).withAnyItemChecked();
listView.performItemClick(null, checkedItemPosition, 0);
assertThat(listView.getCheckedItemPosition()).isEqualTo(checkedItemPosition);
}
@Test
public void givenItemIsNotCheckedAndChoiceModeIsMultiple_whenPerformingItemClick_thenItemShouldBeChecked() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
int positionToClick = anyListIndex();
listView.performItemClick(null, positionToClick, 0);
assertThat(listView.getCheckedItemPositions().get(positionToClick)).isTrue();
assertThat(listView.getCheckedItemCount()).isEqualTo(1);
}
@Test
public void givenItemIsCheckedAndChoiceModeIsMultiple_whenPerformingItemClick_thenItemShouldNotBeChecked() {
prepareListAdapter().withChoiceMode(ListView.CHOICE_MODE_MULTIPLE).withAnyItemChecked();
listView.performItemClick(null, checkedItemPosition, 0);
assertFalse(listView.getCheckedItemPositions().get(checkedItemPosition));
}
private ListAdapterBuilder prepareListAdapter() {
return new ListAdapterBuilder();
}
private ListAdapter prepareWithListAdapter() {
ListAdapter adapter = new ListAdapter("a", "b", "c");
listView.setAdapter(adapter);
shadowOf(listView).populateItems();
return adapter;
}
private ShadowListView prepareListWithThreeItems() {
listView.setAdapter(new ShadowCountingAdapter(3));
shadowOf(listView).populateItems();
return shadowOf(listView);
}
private int anyListIndex() {
return new Random().nextInt(3);
}
private static class ListAdapter extends BaseAdapter {
public List<String> items = new ArrayList<>();
public ListAdapter(String... items) {
this.items.addAll(asList(items));
}
@Override
public int getCount() {
return items.size();
}
@Override
public Object getItem(int position) {
return items.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout linearLayout = new LinearLayout(RuntimeEnvironment.application);
linearLayout.addView(new View(RuntimeEnvironment.application));
return linearLayout;
}
}
public class ListAdapterBuilder {
public ListAdapterBuilder() {
prepareListWithThreeItems();
}
public ListAdapterBuilder withChoiceMode(int choiceMode) {
listView.setChoiceMode(choiceMode);
return this;
}
public ListAdapterBuilder withAnyItemChecked() {
checkedItemPosition = anyListIndex();
listView.setItemChecked(checkedItemPosition, true);
return this;
}
public void withAnyItemsChecked() {
checkedItemPositions = new SparseBooleanArray();
int numberOfSelections = anyListIndex() + 1;
for (int i = 0; i < numberOfSelections; i++) {
checkedItemPositions.put(i, true);
listView.setItemChecked(i, true);
lastCheckedPosition = i;
}
}
}
}