package io.selendroid.server.model.internal;
import android.view.View;
import android.view.ViewGroup;
import com.android.internal.util.Predicate;
import io.selendroid.server.model.AndroidElement;
import io.selendroid.server.model.AndroidNativeElement;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class AbstractNativeElementContextTest {
final static int NO_MATCH = 0;
final static int MATCH = 42;
final static Predicate<View> FIND_MATCH = new TestIdPredicate(MATCH);
/**
* No nodes matches.
*
* a
* / \
* b c
*/
@Test
public void itFindsNothingIfNoNodeMatches() {
ViewGroup b = ignore();
ViewGroup c = ignore();
ViewGroup a = ignore(b, c);
List<View> views = searchViews(a);
assertEquals(0, views.size());
}
/**
* Root matches and no children.
*
* A (1)
*/
@Test
public void itFindsTheRoot() {
ViewGroup root = match();
List<View> views = searchViews(root);
assertFoundViews(views, root);
}
/**
* Root, doesn't match, but children on various levels do.
*
* a
* / \
* b C (1)
* / \ \
* D(2) e F (3)
*/
@Test
public void itFindsChildrenOfTheRoot() {
// Bottom
ViewGroup D = match();
ViewGroup e = ignore();
ViewGroup F = match();
// Middle
ViewGroup b = ignore(D, e);
ViewGroup C = match(F);
// Root
ViewGroup root = ignore(b, C);
List<View> views = searchViews(root);
assertFoundViews(views, C, D, F);
}
private static void assertFoundViews(List<View> foundViews, View ...expectedViewsInOrder) {
assertEquals(expectedViewsInOrder.length, foundViews.size());
for (int i = 0; i < expectedViewsInOrder.length; i++) {
assertEquals(expectedViewsInOrder[i], foundViews.get(i));
}
}
/**
* @return A node that gets returned by the search
*/
private ViewGroup match(ViewGroup ...children) {
return mockViewGroup(MATCH, children);
}
/**
* @return A node that does not get returned by the search
*/
private ViewGroup ignore(ViewGroup ...children) {
return mockViewGroup(NO_MATCH, children);
}
/**
* Helper to do the actual searching.
*/
private static List<View> searchViews(View root) {
List<AndroidElement> elements = AbstractNativeElementContext.searchViews(
mockContext(),
root,
FIND_MATCH,
false);
List<View> foundViews = new ArrayList<View>();
for (AndroidElement element : elements) {
foundViews.add(((AndroidNativeElement) element).getView());
}
return foundViews;
}
private static AbstractNativeElementContext mockContext() {
AbstractNativeElementContext context = mock(AbstractNativeElementContext.class);
when(context.newAndroidElement(any(View.class))).then(new Answer<AndroidElement>() {
@Override
public AndroidElement answer(InvocationOnMock invocationOnMock) throws Throwable {
AndroidNativeElement element = mock(AndroidNativeElement.class);
when(element.getView()).thenReturn((ViewGroup) invocationOnMock.getArguments()[0]);
return element;
}
});
return context;
}
static class TestIdPredicate implements Predicate<View> {
private int id;
protected TestIdPredicate(int id) {
this.id = id;
}
@Override
public boolean apply(View view) {
return view.getId() == id;
}
}
private ViewGroup mockViewGroup(int id, ViewGroup ...viewChildren) {
ViewGroup view = mock(ViewGroup.class);
final ArrayList<ViewGroup> children = new ArrayList<ViewGroup>(Arrays.asList(viewChildren));
when(view.getId()).thenReturn(id);
when(view.getChildCount()).thenReturn(children.size());
when(view.getChildAt(anyInt())).then(new Answer<View>() {
@Override
public View answer(InvocationOnMock invocationOnMock) throws Throwable {
return children.get((Integer) invocationOnMock.getArguments()[0]);
}
});
return view;
}
}