-
Notifications
You must be signed in to change notification settings - Fork 3k
Testing #158
base: webview
Are you sure you want to change the base?
Testing #158
Changes from 18 commits
ee10b26
6973b6b
efcf673
d6836d6
c2de65f
761ecf0
6db9c65
55a9608
3e2c009
d70a861
47f53af
f429a26
bc9ae5f
f0b47b9
5dc103e
2c773f8
403446f
77ff7e0
7185787
61c895e
66b3c49
da570e4
4323afe
aa3df09
6d218b7
f263800
42111ec
d42ded6
0d4fdf8
85d21b8
637fec3
2c83302
3a9d8fa
f8b0c11
be2899c
7c3b2c1
60105b6
e9e7a72
4934ed2
d7806ed
13d0b17
ce63069
fa60356
b4c6e58
35b902d
77350b7
00b047b
048851d
7f7152e
bbc334b
4528470
fa580f4
684f2ef
016ac01
c920025
e7bb953
84cea6b
5c447de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
/* | ||
* Copyright (C) 2020 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.android.samples.webviewdemo | ||
import android.content.Context | ||
import android.os.Looper | ||
import android.webkit.WebView | ||
import androidx.test.core.app.ApplicationProvider | ||
import androidx.test.espresso.web.assertion.WebViewAssertions.webMatches | ||
import androidx.test.espresso.web.model.Atoms.castOrDie | ||
import androidx.test.espresso.web.model.Atoms.script | ||
import androidx.test.espresso.web.sugar.Web.onWebView | ||
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement | ||
import androidx.test.espresso.web.webdriver.DriverAtoms.getText | ||
import androidx.test.espresso.web.webdriver.DriverAtoms.webClick | ||
import androidx.test.espresso.web.webdriver.Locator | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import androidx.test.rule.ActivityTestRule | ||
import junit.framework.Assert.assertEquals | ||
import junit.framework.Assert.assertTrue | ||
import org.hamcrest.CoreMatchers.`is` | ||
import org.hamcrest.CoreMatchers.containsString | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
|
||
/** | ||
* Launch, interact, and verify conditions in an activity that has a WebView instance. | ||
*/ | ||
@RunWith(AndroidJUnit4::class) | ||
|
||
|
||
class MainActivityTest { | ||
|
||
val context = ApplicationProvider.getApplicationContext<Context>() | ||
|
||
@Rule @JvmField | ||
val mainActivityRule = ActivityTestRule(MainActivity::class.java) | ||
fun afterActivityLaunched() { | ||
// Technically we do not need to do this - MainActivity has javascript turned on. | ||
// Other WebViews in your app may have javascript turned off, however since the only way | ||
// to automate WebViews is through javascript, it must be enabled. | ||
onWebView().forceJavascriptEnabled() | ||
} | ||
|
||
// Test to check that the drop down menu behaves as expected | ||
@Test | ||
fun dropDownMenu_SanFran() { | ||
mainActivityRule.getActivity() | ||
onWebView() | ||
.withElement(findElement(Locator.ID, "location")) | ||
.perform(webClick()) // Similar to perform(click()) | ||
.withElement(findElement(Locator.ID, "SF")) | ||
.perform(webClick()) // Similar to perform(click()) | ||
.withElement(findElement(Locator.ID, "title")) | ||
.check(webMatches(getText(), containsString("San Francisco"))) | ||
} | ||
|
||
// Test for checking createJsObject | ||
@Test | ||
fun jsObjectIsInjectedAndContainsPostMessage() { | ||
mainActivityRule.getActivity() | ||
onWebView() | ||
.check( | ||
webMatches( | ||
script("return jsObject && jsObject.postMessage != null;", castOrDie(Boolean::class.javaObjectType)), | ||
`is`(true) | ||
) | ||
) | ||
} | ||
|
||
@Test | ||
fun valueInCallback_compareValueInput_returnsTrue(){ | ||
mainActivityRule.getActivity() | ||
|
||
// Setup | ||
val webView = WebView(context) | ||
val jsObjName = "jsObject" | ||
val allowedOriginRules = setOf<String>("https://example.com") | ||
|
||
// Create HTML | ||
val htmlPage = "<!DOCTYPE html><html><body>" + " <script>" + " myObject.postMessage('hello');" + " </script>" + "</body></html>" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Swap val htmlPage = """
<!DOCTYPE html>
<html>
<body>
<script>${jsObjName}.postMessage('hello');</script>
</body>
</html>
"""
See https://kotlinlang.org/docs/reference/idioms.html#string-interpolation and https://realkotlin.com/tutorials/2018-06-26-multiline-string-literals-in-kotlin/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that it is easier to read if we do something along these lines and get rid fo the
Is that correct? |
||
|
||
// Create JsObject | ||
MainActivity.createJsObject( | ||
webView, | ||
jsObjName, | ||
allowedOriginRules | ||
) { message -> MainActivity.invokeShareIntent(message) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'll want to change this line. Instead of invoking the share intent, you should save I'm not sure if there's a coroutine-friendly way to do this. |
||
|
||
//Inject JsObject into Html | ||
webView.loadData(htmlPage, "text/html", "UTF-8") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Android Views are all single-threaded, so they should only be interacted with on the UI thread, not the test thread. In WebView team's own tests, we have our own helper class to post stuff to the UI thread. In your case, you could just copy the second suggestion from https://stackoverflow.com/a/11125271 (the one with Here's some resources:
|
||
|
||
|
||
//Call js code to invoke callback (in script tag of htmlPage) | ||
gcoleman799 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// evaluate what comes out -> it should be hello | ||
// *Note: "response from callback" is a place holder here I am unsure what should be placed there | ||
assertEquals("response from callback", "hello") | ||
|
||
} | ||
|
||
@Test | ||
// Checks that postMessage runs on the UI thread | ||
fun checkingThreadCallbackRunsOn() { | ||
mainActivityRule.getActivity() | ||
|
||
// Setup | ||
val webView = WebView(context) | ||
val jsObjName = "jsObject" | ||
val allowedOriginRules = setOf<String>("https://example.com") | ||
|
||
// Create HTML | ||
val htmlPage = | ||
"<!DOCTYPE html><html><body>" + " <script>" + " jsObject.postMessage('hello');" + " </script>" + "</body></html>" | ||
|
||
|
||
// Create JsObject | ||
MainActivity.createJsObject( | ||
webView, | ||
jsObjName, | ||
allowedOriginRules | ||
) { message -> MainActivity.invokeShareIntent(message) } | ||
|
||
|
||
// Inject JsObject into Html by loading it in the webview | ||
webView.loadData(htmlPage, "text/html", "UTF-8") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should swap this out for |
||
|
||
|
||
// Use coroutine to go onto UI thread here? | ||
// Call js code to invoke callback (in script tag of htmlPage) | ||
|
||
|
||
// check that method is running on the UI thread | ||
assertTrue(isUiThread()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want this assert here because instrumentation tests run on the "test thread," not the UI thread. I wrote an internal google doc which goes into more details about this. As a first step, try moving this into createJsObject's callback. That will return correct results, but comes with the drawback that failing assertion crashes the process instead of showing up as a failed test (the google doc has more context on this). If you can get that working, then you can improve this further by using a |
||
} | ||
|
||
/** | ||
* Returns true if the current thread is the UI thread based on the | ||
* Looper. | ||
*/ | ||
private fun isUiThread(): Boolean { | ||
return Looper.myLooper() == Looper.getMainLooper() | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.