package com.mopub.mobileads; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.net.Uri; import android.webkit.WebView; import com.mopub.common.MoPubBrowser; import com.mopub.common.test.support.SdkTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.robolectric.Robolectric; import static com.mopub.mobileads.MoPubErrorCode.UNSPECIFIED; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.stub; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(SdkTestRunner.class) public class HtmlWebViewClientTest { private HtmlWebViewClient subject; private HtmlWebViewListener htmlWebViewListener; private BaseHtmlWebView htmlWebView; private Context context; @Before public void setUp() throws Exception { htmlWebViewListener = mock(HtmlWebViewListener.class); htmlWebView = mock(BaseHtmlWebView.class); context = Robolectric.buildActivity(Activity.class).create().get().getApplicationContext(); when(htmlWebView.getContext()).thenReturn(context); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, "clickthrough", "redirect"); } @Test public void shouldOverrideUrlLoading_withMoPubFinishLoad_shouldCallAdDidLoad() throws Exception { boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopub://finishLoad"); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onLoaded(eq(htmlWebView)); } @Test public void shouldOverrideUrlLoading_withMoPubClose_shouldCallAdDidClose() throws Exception { boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopub://close"); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onCollapsed(); } @Test public void shouldOverrideUrlLoading_withMoPubFailLoad_shouldCallLoadFailUrl() throws Exception { boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopub://failLoad"); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onFailed(UNSPECIFIED); } @Test public void shouldOverrideUrlLoading_withMoPubCustom_withUserClick_shouldStartCustomIntent() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopub://custom?fnc=myFnc&data=myData"); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity).isNotNull(); assertThat(startedActivity.getAction()).isEqualTo("myFnc"); assertThat(startedActivity.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0); assertThat(startedActivity.getStringExtra(HtmlBannerWebView.EXTRA_AD_CLICK_DATA)).isEqualTo("myData"); } @Test public void shouldOverrideUrlLoading_withMoPubCustom_withoutUserClick_shouldNotStartActivity() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopub://custom?fnc=myFnc&data=myData"); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity).isNull(); } @Test public void shouldOverrideUrlLoading_withMoPubCustomAndNullData_withUserClick_shouldStartCustomIntent() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopub://custom?fnc=myFnc"); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity).isNotNull(); assertThat(startedActivity.getAction()).isEqualTo("myFnc"); assertThat(startedActivity.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0); assertThat(startedActivity.getStringExtra(HtmlBannerWebView.EXTRA_AD_CLICK_DATA)).isNull(); } @Test public void shouldOverrideUrlLoading_withMoPubCustomAndNullData_withoutUserClick_shouldNotStartCustomIntent() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopub://custom?fnc=myFnc"); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity).isNull(); } @Test public void shouldOverrideUrlLoading_withPhoneIntent_shouldStartDefaultIntent() throws Exception { assertPhoneUrlStartedCorrectIntent("tel:"); assertPhoneUrlStartedCorrectIntent("voicemail:"); assertPhoneUrlStartedCorrectIntent("sms:"); assertPhoneUrlStartedCorrectIntent("mailto:"); assertPhoneUrlStartedCorrectIntent("geo:"); assertPhoneUrlStartedCorrectIntent("google.streetview:"); } @Test public void shouldOverrideUrlLoading_withCustomApplicationIntent_withUserClick_andCanHandleCustomIntent_shouldTryToLaunchCustomIntent() throws Exception { String customUrl = "myintent://something"; stub(htmlWebView.wasClicked()).toReturn(true); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); Robolectric.packageManager.addResolveInfoForIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(customUrl)), new ResolveInfo()); boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, customUrl); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); Intent intent = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(intent).isNotNull(); } @Test public void shouldOverrideUrlLoading_withCustomApplicationIntent_withoutUserClick_shouldNotTryToLaunchIntent() throws Exception { String customUrl = "myintent://something"; stub(htmlWebView.wasClicked()).toReturn(false); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, customUrl); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void shouldOverrideUrlLoading_withCustomApplicationIntent_withUserClick_butCanNotHandleCustomIntent_shouldDefaultToMoPubBrowser() throws Exception { String customUrl = "myintent://something"; stub(htmlWebView.wasClicked()).toReturn(true); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, customUrl); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); Intent startedIntent = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedIntent).isNotNull(); assertThat(startedIntent.getComponent().getClassName()).isEqualTo("com.mopub.common.MoPubBrowser"); } @Test public void shouldOverrideUrlLoading_withHttpUrl_withUserClick_shouldOpenBrowser() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); String validUrl = "http://www.mopub.com"; boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, validUrl); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity.getComponent().getClassName()).isEqualTo("com.mopub.common.MoPubBrowser"); assertThat(startedActivity.getStringExtra(MoPubBrowser.DESTINATION_URL_KEY)).isEqualTo(validUrl); assertThat(startedActivity.getData()).isNull(); } @Test public void shouldOverrideUrlLoading_withHttpUrl_withoutUserClick_shouldNotOpenBrowser() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); String validUrl = "http://www.mopub.com"; boolean didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, validUrl); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void shouldOverrideUrlLoading_withClickTrackingRedirect_withUserClick_shouldNotChangeUrl() throws Exception { String validUrl = "http://www.mopub.com"; stub(htmlWebView.wasClicked()).toReturn(true); subject.shouldOverrideUrlLoading(htmlWebView, validUrl); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity.getStringExtra(MoPubBrowser.DESTINATION_URL_KEY)).isEqualTo(validUrl); } @Test public void shouldOverrideUrlLoading_withClickTrackingRedirect_withoutUserClick_shouldChangeUrl() throws Exception { String validUrl = "http://www.mopub.com"; stub(htmlWebView.wasClicked()).toReturn(false); subject.shouldOverrideUrlLoading(htmlWebView, validUrl); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void shouldOverrideUrlLoading_withEmptyUrl_withUserClick_shouldLoadAboutBlank() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); subject.shouldOverrideUrlLoading(htmlWebView, ""); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity.getComponent().getClassName()).isEqualTo("com.mopub.common.MoPubBrowser"); assertThat(startedActivity.getStringExtra(MoPubBrowser.DESTINATION_URL_KEY)).isEqualTo("about:blank"); assertThat(startedActivity.getData()).isNull(); } @Test public void shouldOverrideUrlLoading_withEmptyUrl_withoutUserClick_shouldLoadAboutBlank() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); subject.shouldOverrideUrlLoading(htmlWebView, ""); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void shouldOverrideUrlLoading_withNativeBrowserScheme_withUserClick_shouldStartIntentWithActionView() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean shouldOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopubnativebrowser://navigate?url=http%3A%2F%2Fwww.mopub.com"); assertThat(shouldOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity.getAction()).isEqualTo("android.intent.action.VIEW"); assertThat(startedActivity.getData().toString()).isEqualTo("http://www.mopub.com"); } @Test public void shouldOverrideUrlLoading_withNativeBrowserScheme_withoutUserClick_shouldStartIntentWithActionView() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean shouldOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "mopubnativebrowser://navigate?url=http%3A%2F%2Fwww.mopub.com"); assertThat(shouldOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void shouldOverrideUrlLoading_withNativeBrowserScheme_butOpaqueUri_withUserClick_shouldNotBeHandledByNativeBrowser() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); String opaqueNativeBrowserUriString = "mopubnativebrowser:navigate?url=http%3A%2F%2Fwww.mopub.com"; subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean shouldOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, opaqueNativeBrowserUriString); assertThat(shouldOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity.getComponent().getClassName()).isEqualTo("com.mopub.common.MoPubBrowser"); assertThat(startedActivity.getStringExtra(MoPubBrowser.DESTINATION_URL_KEY)).isEqualTo(opaqueNativeBrowserUriString); assertThat(startedActivity.getData()).isNull(); } @Test public void shouldOverrideUrlLoading_withNativeBrowserScheme_butOpaqueUri_withoutUserClick_shouldNotLoad() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); String opaqueNativeBrowserUriString = "mopubnativebrowser:navigate?url=http%3A%2F%2Fwww.mopub.com"; subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean shouldOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, opaqueNativeBrowserUriString); assertThat(shouldOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void shouldOverrideUrlLoading_withNativeBrowserScheme_withInvalidHostSchemeUrl_withUserClick_shouldNotInvokeNativeBrowser() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean shouldOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "something://blah?url=invalid"); assertThat(shouldOverrideUrl).isTrue(); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity.getAction()).isNotEqualTo("android.intent.action.VIEW"); verify(htmlWebViewListener).onClicked(); } @Test public void shouldOverrideUrlLoading_withNativeBrowserScheme_withInvalidHostSchemeUrl_withoutUserClick_shouldNotInvokeNativeBrowser() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, null); boolean shouldOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, "something://blah?url=invalid"); assertThat(shouldOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void onPageStarted_whenLoadedUrlStartsWithRedirect_withUserClick_shouldOpenInBrowser() throws Exception { String url = "redirectUrlToLoad"; stub(htmlWebView.wasClicked()).toReturn(true); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, "redirect"); WebView view = mock(WebView.class); subject.onPageStarted(view, url, null); verify(view).stopLoading(); Intent intent = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(intent.getStringExtra(MoPubBrowser.DESTINATION_URL_KEY)).isEqualTo(url); assertThat(intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0); assertThat(intent.getComponent().getClassName()).isEqualTo("com.mopub.common.MoPubBrowser"); } @Test public void onPageStarted_whenLoadedUrlStartsWithRedirect_withoutUserClick_shouldOpenInBrowser() throws Exception { String url = "redirectUrlToLoad"; stub(htmlWebView.wasClicked()).toReturn(false); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, null, "redirect"); WebView view = mock(WebView.class); subject.onPageStarted(view, url, null); verify(view).stopLoading(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void onPageStarted_whenLoadedUrlStartsWithRedirectAndHasClickthrough_withUserClick_shouldNotChangeUrl_shouldOpenInBrowser() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); String url = "redirectUrlToLoad"; WebView view = mock(WebView.class); subject.onPageStarted(view, url, null); verify(view).stopLoading(); Intent intent = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(intent.getStringExtra(MoPubBrowser.DESTINATION_URL_KEY)).isEqualTo(url); assertThat(intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0); assertThat(intent.getComponent().getClassName()).isEqualTo("com.mopub.common.MoPubBrowser"); } @Test public void onPageStarted_whenLoadedUrlStartsWithRedirectAndHasClickthrough_withoutUserClick_shouldNotOpenInBrowser() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); String url = "redirectUrlToLoad"; WebView view = mock(WebView.class); subject.onPageStarted(view, url, null); verify(view).stopLoading(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void onPageStarted_whenLoadedUrlStartsWithRedirectAndHasClickthrough_withUserClick_whenMoPubBrowserCannotHandleIntent_shouldOpenInNativeBrowser() throws Exception { Context mockContext = mock(Context.class); stub(htmlWebView.wasClicked()).toReturn(true); stub(htmlWebView.getContext()).toReturn(mockContext); String url = "redirectUrlToLoad"; // We only want startActivity() to throw an exception the first time we call it. doThrow(new ActivityNotFoundException()) .doNothing() .when(mockContext).startActivity(any(Intent.class)); subject = new HtmlWebViewClient(htmlWebViewListener, htmlWebView, "clickthrough", "redirect"); subject.onPageStarted(htmlWebView, url, null); ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mockContext, times(2)).startActivity(intentArgumentCaptor.capture()); Intent intent = intentArgumentCaptor.getAllValues().get(1); assertThat(intent.getAction()).isEqualTo("android.intent.action.VIEW"); assertThat(intent.getData().toString()).isEqualTo("about:blank"); assertThat(intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0); verify(htmlWebViewListener, times(1)).onClicked(); } @Test public void onPageStarted_whenLoadedUrlDoesntStartWithRedirect_shouldDoNothing() throws Exception { WebView view = mock(WebView.class); subject.onPageStarted(view, "this doesn't start with redirect", null); verify(view, never()).stopLoading(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void launchIntentForUserClick_shouldStartActivityAndResetClickStatusAndReturnTrue() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); Context context = mock(Context.class); Intent intent = mock(Intent.class); boolean result = subject.launchIntentForUserClick(context, intent, null); verify(context).startActivity(eq(intent)); verify(htmlWebView).onResetUserClick(); assertThat(result).isTrue(); } @Test public void launchIntentForUserClick_whenUserHasNotClicked_shouldNotStartActivityAndReturnFalse() throws Exception { stub(htmlWebView.wasClicked()).toReturn(false); Context context = mock(Context.class); Intent intent = mock(Intent.class); boolean result = subject.launchIntentForUserClick(context, intent, null); verify(context, never()).startActivity(any(Intent.class)); verify(htmlWebView, never()).onResetUserClick(); assertThat(result).isFalse(); } @Test public void launchIntentForUserClick_whenNoMatchingActivity_shouldNotStartActivityAndReturnFalse() throws Exception { Context context = mock(Context.class); Intent intent = mock(Intent.class); stub(htmlWebView.wasClicked()).toReturn(true); doThrow(new ActivityNotFoundException()).when(context).startActivity(any(Intent.class)); boolean result = subject.launchIntentForUserClick(context, intent, null); verify(htmlWebView, never()).onResetUserClick(); assertThat(result).isFalse(); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); } @Test public void launchIntentForUserClick_whenContextIsNull_shouldNotStartActivityAndReturnFalse() throws Exception { stub(htmlWebView.wasClicked()).toReturn(true); Intent intent = new Intent(); boolean result = subject.launchIntentForUserClick(null, intent, null); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); verify(htmlWebView, never()).onResetUserClick(); assertThat(result).isFalse(); } private void assertPhoneUrlStartedCorrectIntent(String url) { boolean didOverrideUrl; stub(htmlWebView.wasClicked()).toReturn(true); didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, url); Intent startedActivity = Robolectric.getShadowApplication().getNextStartedActivity(); assertThat(startedActivity.getAction()).isEqualTo(Intent.ACTION_VIEW); assertThat(startedActivity.getData().toString()).isEqualTo(url); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener).onClicked(); reset(htmlWebViewListener); stub(htmlWebView.wasClicked()).toReturn(false); didOverrideUrl = subject.shouldOverrideUrlLoading(htmlWebView, url); assertThat(Robolectric.getShadowApplication().getNextStartedActivity()).isNull(); assertThat(didOverrideUrl).isTrue(); verify(htmlWebViewListener, never()).onClicked(); reset(htmlWebViewListener); } }