package com.mopub.nativeads;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.mopub.common.test.support.SdkTestRunner;
import com.mopub.common.util.Utils;
import com.mopub.network.Networking;
import com.mopub.volley.Request;
import com.mopub.volley.RequestQueue;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import java.util.List;
import java.util.Map;
import static com.mopub.nativeads.MoPubNative.EMPTY_EVENT_LISTENER;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@RunWith(SdkTestRunner.class)
public class NativeResponseTest {
private NativeResponse subject;
private BaseForwardingNativeAd mNativeAd;
private Activity context;
private ViewGroup view;
private MoPubNative.MoPubNativeListener moPubNativeListener;
private NativeResponse subjectWMockBaseNativeAd;
private NativeAdInterface mMockNativeAd;
private boolean baseNativeAdRecordedImpression;
private boolean baseNativeAdIsClicked;
@Mock
private RequestQueue mockRequestQueue;
@Before
public void setUp() throws Exception {
context = new Activity();
mNativeAd = new BaseForwardingNativeAd() {
@Override
public void recordImpression() {
baseNativeAdRecordedImpression = true;
}
@Override
public void handleClick(@NonNull final View view) {
baseNativeAdIsClicked = true;
}
};
mNativeAd.setTitle("title");
mNativeAd.setText("text");
mNativeAd.setMainImageUrl("mainImageUrl");
mNativeAd.setIconImageUrl("iconImageUrl");
mNativeAd.setClickDestinationUrl("clickDestinationUrl");
mNativeAd.setCallToAction("callToAction");
mNativeAd.addExtra("extra", "extraValue");
mNativeAd.addExtra("extraImage", "extraImageUrl");
mNativeAd.addImpressionTracker("impressionUrl");
mNativeAd.setImpressionMinTimeViewed(500);
view = new LinearLayout(context);
Networking.setRequestQueueForTesting(mockRequestQueue);
moPubNativeListener = mock(MoPubNative.MoPubNativeListener.class);
subject = new NativeResponse(context,
"moPubImpressionTrackerUrl",
"moPubClickTrackerUrl",
"adunit_id", mNativeAd, moPubNativeListener);
mMockNativeAd = mock(NativeAdInterface.class);
subjectWMockBaseNativeAd = new NativeResponse(context,
"moPubImpressionTrackerUrl",
"moPubClickTrackerUrl",
"adunit_id", mMockNativeAd, moPubNativeListener);
}
@Test
public void constructor_shouldSetNativeEventListenerOnNativeAdInterface() {
reset(mMockNativeAd);
subject = new NativeResponse(context, "moPubImpressionTrackerUrl", "moPubClickTrackerUrl",
"adunit_id", mMockNativeAd, moPubNativeListener);
verify(mMockNativeAd).setNativeEventListener(any(BaseForwardingNativeAd.NativeEventListener.class));
}
@Test
public void getTitle_shouldReturnTitleFromBaseNativeAd() {
assertThat(subject.getTitle()).isEqualTo("title");
}
@Test
public void getTitle_shouldReturnTextFromBaseNativeAd() {
assertThat(subject.getText()).isEqualTo("text");
}
@Test
public void getMainImageUrl_shouldReturnMainImageUrlFromBaseNativeAd() {
assertThat(subject.getMainImageUrl()).isEqualTo("mainImageUrl");
}
@Test
public void getIconImageUrl_shouldReturnIconImageUrlFromBaseNativeAd() {
assertThat(subject.getIconImageUrl()).isEqualTo("iconImageUrl");
}
@Test
public void getClickDestinationUrl_shouldReturnClickDestinationUrlFromBaseNativeAd() {
assertThat(subject.getClickDestinationUrl()).isEqualTo("clickDestinationUrl");
}
@Test
public void getCallToAction_shouldReturnCallToActionFromBaseNativeAd() {
assertThat(subject.getCallToAction()).isEqualTo("callToAction");
}
@Test
public void getExtra_shouldReturnExtraFromBaseNativeAd() {
assertThat(subject.getExtra("extra")).isEqualTo("extraValue");
}
@Test
public void getExtras_shouldReturnCopyOfExtrasMapFromBaseNativeAd() {
final Map<String, Object> extras = subject.getExtras();
assertThat(extras.size()).isEqualTo(2);
assertThat(extras.get("extra")).isEqualTo("extraValue");
assertThat(extras.get("extraImage")).isEqualTo("extraImageUrl");
assertThat(extras).isNotSameAs(mNativeAd.getExtras());
}
@Test
public void getImpressionTrackers_shouldReturnImpressionTrackersFromMoPubAndFromBaseNativeAd() {
final List<String> impressionTrackers = subject.getImpressionTrackers();
assertThat(impressionTrackers).containsOnly("moPubImpressionTrackerUrl", "impressionUrl");
}
@Test
public void getImpressionMinTimeViewed_shouldReturnImpressionMinTimeViewedFromBaseNativeAd() {
assertThat(subject.getImpressionMinTimeViewed()).isEqualTo(500);
}
@Test
public void getImpressionMinPercentageViewed_shouldReturnImpressionMinPercentageViewedFromBaseNativeAd() {
assertThat(subject.getImpressionMinPercentageViewed()).isEqualTo(50);
}
@Test
public void getClickTracker_shouldReturnMoPubClickTracker() {
assertThat(subject.getClickTracker()).isEqualTo("moPubClickTrackerUrl");
}
@Test
public void prepare_shouldCallPrepareOnBaseNativeAd() {
subjectWMockBaseNativeAd.prepare(view);
verify(mMockNativeAd).prepare(view);
}
@Test
public void prepare_whenDestroyed_shouldReturnFast() {
subjectWMockBaseNativeAd.destroy();
subjectWMockBaseNativeAd.prepare(view);
verify(mMockNativeAd, never()).prepare(view);
}
@Test
public void prepare_withOverridingeClickTracker_shouldNotSetOnClickListener() throws Exception {
when(mMockNativeAd.isOverridingClickTracker()).thenReturn(true);
View view = mock(View.class);
subjectWMockBaseNativeAd.prepare(view);
verify(view, never()).setOnClickListener(any(NativeResponse.NativeViewClickListener.class));
}
@Test
public void prepare_withoutOverridingClickTracker_shouldSetOnClickListener() throws Exception {
when(mMockNativeAd.isOverridingClickTracker()).thenReturn(false);
View view = mock(View.class);
subjectWMockBaseNativeAd.prepare(view);
verify(view).setOnClickListener(any(NativeResponse.NativeViewClickListener.class));
}
@Test
public void prepare_shouldAttachClickListenersToViewTree() {
RelativeLayout relativeLayout = new RelativeLayout(context);
Button callToActionView = new Button(context);
callToActionView.setId((int) Utils.generateUniqueId());
relativeLayout.addView(callToActionView);
assertThat(relativeLayout.performClick()).isFalse();
assertThat(callToActionView.performClick()).isFalse();
subject.prepare(relativeLayout);
assertThat(relativeLayout.performClick()).isTrue();
assertThat(callToActionView.performClick()).isTrue();
}
@Test
public void recordImpression_shouldRecordImpressionsAndCallIntoBaseNativeAdAndNotifyListenerIdempotently() {
assertThat(subject.getRecordedImpression()).isFalse();
subject.recordImpression(view);
assertThat(subject.getRecordedImpression()).isTrue();
assertThat(baseNativeAdRecordedImpression).isTrue();
verify(moPubNativeListener).onNativeImpression(view);
// There are two impression trackers here.
verify(mockRequestQueue, times(2)).add(any(Request.class));
// reset state
baseNativeAdRecordedImpression = false;
reset(moPubNativeListener);
reset(mockRequestQueue);
// verify impression tracking doesn't fire again
subject.recordImpression(view);
assertThat(subject.getRecordedImpression()).isTrue();
assertThat(baseNativeAdRecordedImpression).isFalse();
verify(moPubNativeListener, never()).onNativeImpression(view);
verify(mockRequestQueue, never()).add(any(Request.class));
}
@Test
public void recordImpression_whenDestroyed_shouldReturnFast() {
subject.destroy();
subject.recordImpression(view);
assertThat(subject.getRecordedImpression()).isFalse();
assertThat(baseNativeAdRecordedImpression).isFalse();
verify(moPubNativeListener, never()).onNativeImpression(view);
verify(mockRequestQueue, never()).add(any(Request.class));
}
@Test
public void handleClick_withNoBaseNativeAdClickDestinationUrl_shouldRecordClickAndCallIntoBaseNativeAdAndNotifyListener() {
assertThat(subject.isClicked()).isFalse();
subject.handleClick(view);
assertThat(subject.isClicked()).isTrue();
assertThat(baseNativeAdIsClicked).isTrue();
verify(moPubNativeListener).onNativeClick(view);
verify(mockRequestQueue).add(any(Request.class));
// reset state
baseNativeAdIsClicked = false;
reset(moPubNativeListener);
reset(mockRequestQueue);
// second time, tracking does not fire
subject.handleClick(view);
assertThat(subject.isClicked()).isTrue();
assertThat(baseNativeAdRecordedImpression).isFalse();
verify(moPubNativeListener).onNativeClick(view);
verifyZeroInteractions(mockRequestQueue);
}
@Ignore("pending")
@Test
public void handleClick_withBaseNativeAdClickDestinationUrl_shouldRecordClickAndCallIntoBaseNativeAdAndOpenClickDestinationAndNotifyListener() {
// Really difficult to test url resolution since it doesn't use the apache http client
}
@Test
public void handleClick_whenDestroyed_shouldReturnFast() {
subject.destroy();
subject.handleClick(view);
assertThat(subject.isClicked()).isFalse();
assertThat(baseNativeAdIsClicked).isFalse();
verify(moPubNativeListener, never()).onNativeClick(view);
verifyZeroInteractions(mockRequestQueue);
}
@Test
public void destroy_shouldCallIntoBaseNativeAd() {
subjectWMockBaseNativeAd.destroy();
assertThat(subjectWMockBaseNativeAd.isDestroyed()).isTrue();
verify(mMockNativeAd).destroy();
reset(mMockNativeAd);
subjectWMockBaseNativeAd.destroy();
verify(mMockNativeAd, never()).destroy();
}
@Test
public void destroy_shouldSetMoPubNativeEventListenerToEmptyMoPubNativeListener() {
assertThat(subjectWMockBaseNativeAd.getMoPubNativeEventListener()).isSameAs(moPubNativeListener);
subjectWMockBaseNativeAd.destroy();
assertThat(subjectWMockBaseNativeAd.getMoPubNativeEventListener()).isSameAs(EMPTY_EVENT_LISTENER);
}
// NativeViewClickListener tests
@Test
public void NativeViewClickListener_onClick_shouldQueueClickTrackerAndUrlResolutionTasks() {
subject = mock(NativeResponse.class);
NativeResponse.NativeViewClickListener nativeViewClickListener = subject.new NativeViewClickListener();
View view = new View(context);
nativeViewClickListener.onClick(view);
verify(subject).handleClick(view);
}
@Ignore("pending")
@Test
public void loadExtrasImage_shouldAsyncLoadImages() {
// no easy way to test this since nothing can be mocked
// also not a critical test since it directly calls another service
}
}