package com.jakewharton.rxbinding2.support.v7.widget;
import android.app.Instrumentation;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.Espresso;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.jakewharton.rxbinding.ViewDirtyIdlingResource;
import com.jakewharton.rxbinding2.RecordingObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(AndroidJUnit4.class)
public final class RxRecyclerViewTest {
@Rule public final ActivityTestRule<RxRecyclerViewTestActivity> activityRule =
new ActivityTestRule<>(RxRecyclerViewTestActivity.class);
private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
RecyclerView view;
private ViewDirtyIdlingResource viewDirtyIdler;
private View child;
@Before public void setUp() {
RxRecyclerViewTestActivity activity = activityRule.getActivity();
view = activity.recyclerView;
child = new View(activityRule.getActivity());
viewDirtyIdler = new ViewDirtyIdlingResource(activity);
Espresso.registerIdlingResources(viewDirtyIdler);
}
@After public void tearDown() {
Espresso.unregisterIdlingResources(viewDirtyIdler);
}
@Test public void childAttachEvents() {
RecordingObserver<RecyclerViewChildAttachStateChangeEvent> o = new RecordingObserver<>();
RxRecyclerView.childAttachStateChangeEvents(view)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(o);
o.assertNoMoreEvents();
final SimpleAdapter adapter = new SimpleAdapter(child);
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.setAdapter(adapter);
}
});
assertEquals(RecyclerViewChildAttachEvent.create(view, child), o.takeNext());
o.dispose();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.setAdapter(adapter);
}
});
o.assertNoMoreEvents();
}
@Test public void childDetachEvents() {
final SimpleAdapter adapter = new SimpleAdapter(child);
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.setAdapter(adapter);
}
});
RecordingObserver<RecyclerViewChildAttachStateChangeEvent> o = new RecordingObserver<>();
RxRecyclerView.childAttachStateChangeEvents(view)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(o);
o.assertNoMoreEvents();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.setAdapter(null);
}
});
assertEquals(RecyclerViewChildDetachEvent.create(view, child), o.takeNext());
o.dispose();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.setAdapter(adapter);
}
});
o.assertNoMoreEvents();
}
@Test public void scrollEventsVertical() {
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.setAdapter(new Adapter());
}
});
RecordingObserver<RecyclerViewScrollEvent> o = new RecordingObserver<>();
RxRecyclerView.scrollEvents(view)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(o);
o.assertNoMoreEvents();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(0, 50);
}
});
RecyclerViewScrollEvent event1 = o.takeNext();
assertNotNull(event1);
assertEquals(50, event1.dy());
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(0, 0);
}
});
o.assertNoMoreEvents();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(0, -50);
}
});
RecyclerViewScrollEvent event2 = o.takeNext();
assertNotNull(event2);
assertEquals(-50, event2.dy());
// Back at position 0. Trying to scroll earlier shouldn't fire any events
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(0, -50);
}
});
o.assertNoMoreEvents();
o.dispose();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(0, 50);
}
});
o.assertNoMoreEvents();
}
@Test public void scrollEventsHorizontal() {
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.setAdapter(new Adapter());
((LinearLayoutManager) view.getLayoutManager()).setOrientation(LinearLayoutManager.HORIZONTAL);
}
});
instrumentation.waitForIdleSync();
RecordingObserver<RecyclerViewScrollEvent> o = new RecordingObserver<>();
RxRecyclerView.scrollEvents(view)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(o);
o.assertNoMoreEvents();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(50, 0);
}
});
RecyclerViewScrollEvent event3 = o.takeNext();
assertNotNull(event3);
assertEquals(50, event3.dx());
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(0, 0);
}
});
o.assertNoMoreEvents();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(-50, 0);
}
});
RecyclerViewScrollEvent event4 = o.takeNext();
assertNotNull(event4);
assertEquals(-50, event4.dx());
// Back at position 0. Trying to scroll earlier shouldn't fire any events
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(-50, 0);
}
});
o.assertNoMoreEvents();
o.dispose();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
view.scrollBy(50, 0);
}
});
o.assertNoMoreEvents();
}
private class SimpleAdapter extends RecyclerView.Adapter {
private final View child;
SimpleAdapter(View child) {
this.child = child;
}
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RecyclerView.ViewHolder(child) {
};
}
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
@Override public int getItemCount() {
return 1;
}
}
private static class Adapter extends RecyclerView.Adapter<ViewHolder> {
public Adapter() {
setHasStableIds(true);
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
TextView v = (TextView) LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
return new ViewHolder(v);
}
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.textView.setText(String.valueOf(position));
}
@Override public int getItemCount() {
return 100;
}
@Override public long getItemId(int position) {
return position;
}
}
private static class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ViewHolder(TextView itemView) {
super(itemView);
this.textView = itemView;
}
}
}