Check WebView's URL while Unit Testing in Robotium - java

Im trying to figure out the easiest way to check my Main Activity's URL inside my (WebView). This is going to be in a Unit Test using the Robotium 4.1 framework. All i am trying to figure out how i can interact with the webview, such as send the activity a URL to load, so i can test against that and make sure the correct URL is loaded and also check my actionbar items as well.
Any Codes, suggestions or recommendations would be highly appreciated.
public class ErrorHandling extends
ActivityInstrumentationTestCase2<Webview_Main> {
private Solo solo;
private WebView web;
..
protected void setUp() throws Exception {
super.setUp();
solo = new Solo(getInstrumentation(),getActivity());
}
Updated per recommendations (Currently - NullPointerException Erorrs)
public void testUrl() {
String URL = solo.getString(com.example.webview.R.string.url);
web = (WebView) getActivity().findViewById(R.id.webview_main); //Added
try {
solo.assertCurrentActivity("Launching Main Activity", Webview_Main.class);
assertTrue(solo.getWebUrl().equals("URL")); }
catch (Error error) {
}
}
public void testWeb() {
try {
web = (WebView) getActivity().findViewById(R.id.webview_main); //Added
solo.getCurrentActivity().runOnUiThread(new Runnable() {
public void run() {
web.loadUrl("http://www.google.com");
}
});
solo.sleep(1000);
} catch (Error error) {
}
}

first, calling
web.getSettings();
gives you nothing. It provides you WebSettings, but you don't do anything with that object.
To assert current url in your webview you can use:
assertTrue(solo.getWebUrl().equals("your url"));
To load specified url in your webview you have to run it in another thread (here I'm not sure, if it should be runOnMainSync or runOnUiThread, however I tried runOnUiThread and it worked fine), for instance:
solo.getCurrentActivity().runOnUiThread(new Runnable() {
public void run() {
web.loadUrl("http://www.OTHER_url_TO_test_AGAINST");
}
});

The steps to check the URL are as follows:
1) Create the webview object for the specific application
obj = (WebView)getActivity().findViewById((package name of source or apk).R.id.(Id of the source webview))
Note: If you dont have the source code of the source application of whome you are writing the test case then use the hierarchy viewer to find the id of the webview.
2) Add some sleep so that it will wait the time taken to load the url
solo.sleep(time)
3) Use this to get the current value or status of the application.
obj.getSettings()
4) Take a Boolean variable and store the value of
Boolean variable = solo.getWebUrl().equals("URL to be tested");
5) assertTrue(Boolean variable)
To verify the value true or false.

Related

How to Access Activity from ActivityScenarioRule

I am using ActivityScenarioRule for Espresso UI Testing and I wanted to get access to the method getStringArray(), calling which requires the Activity . So, is there any way to retrieve the Activity by the ActivityScenarioRule , maybe something similar to getActivity in ActivityTestRule.
#Rule
public ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
I am not using ActivityTestRule, because it is deprecated!
Since it appears you're using Java, here's how you'd do it:
#Rule
ActivityScenarioRule<MainActivity> activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
#Test
public void test() {
activityScenarioRule.getScenario().onActivity(activity -> {
// use 'activity'.
});
}
Please read the documentation for more info on these new ways of interacting with the activity under test.
For anyone who wants Activity, but that without need to re-write all tests to run on UI-thread, a fairly straightforward Java way to get it:
Waiting for UI
Assume you want to test if a dialog is shown after some delay, the onActivity(...) hook runs on UI-thread, which means waiting in there would cause the dialog to be nerver shown.
In such cases you need to keep a strong-reference to ActivityScenario (as that prevents Activity close).
Test should wait for onActivity(...) hook to be called, then keep passed Activity's reference.
Finally, move test logic out of onActivity(...) hook.
Example
private ActivityScenario mActivityScenario;
#After
public void tearDown() throws Exception {
if (mActivityScenario != null) {
mActivityScenario.close();
}
mActivityScenario = null;
}
#Override
public Activity getActivity() {
if (mActivityScenario == null) {
mActivityScenario = ActivityScenario.launch(getActivityClassForScenario());
}
return tryAcquireScenarioActivity(mActivityScenario);
}
protected static Activity tryAcquireScenarioActivity(ActivityScenario activityScenario) {
Semaphore activityResource = new Semaphore(0);
Activity[] scenarioActivity = new Activity[1];
activityScenario.onActivity(activity -> {
scenarioActivity[0] = activity;
activityResource.release();
});
try {
activityResource.tryAcquire(15000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail("Failed to acquire activity scenario semaphore");
}
Assert.assertNotNull("Scenario Activity should be non-null", scenarioActivity[0]);
return scenarioActivity[0];
}
Espresso states the following:
At the same time, the framework prevents direct access to activities
and views of the application because holding on to these objects and
operating on them off the UI thread is a major source of test
flakiness.
When there is no other way I use the following method to get an arbitrary activity from an ActivityScenarioRule. It uses onActivity mentioned in the accepted answer:
private <T extends Activity> T getActivity(ActivityScenarioRule<T> activityScenarioRule) {
AtomicReference<T> activityRef = new AtomicReference<>();
activityScenarioRule.getScenario().onActivity(activityRef::set);
return activityRef.get();
}
Any onView(...) code inside onActivity led to a timeout in my testcases. So, I extracted the activity and used it with success outside the onActivity. Beware tho! See the statement above.
#Test
fun checkForUpdate() {
val scenario = ActivityScenario.launch(MainActivity::class.java)
scenario.onActivity {
UpdateTool.checkForUpdate(it)
}
}

Java FX 8 using Platform.runLater() in Application init() method to display Alert / Login boxes

Please bear with me as this is going to be a pretty detailed and specific requirement. I also looked into numerous questions already listed on stackoverflow regarding Platform.runLater() usage. But I couldn't find an appropriate response. If someone can identify the correct question and mark this as a duplicate I would be great full all the same. Also I'm a newbie to JavaFX programming, so if I need to modify the architecture, please let me know.
I'm creating a stand-alone JavaFX application to run some unit tests based on user-defined requirements. Before running the actual tests, I need to make sure that the end-user is using the test with latest libraries available, by comparing the manifest files from remote Artifactory repo and the manifest files from jars on the current classpath. I want to store the credentials for Artifactory so that I can reuse them for subsequent executions. If the build numbers from remote and local manifest doesn't match, then I want to display an Error Alert window informing the user. I want to use a pre-loader to display a gif and appropriate messages in each of these steps.
public class UI extends Application
{
String username=null, password=null;
Manifest remoteManifest, localManifest;
#Override
public void init() {
notifyPreloader("Loading Stored Credentials");
loadStoredCredentials();
if(username == null && password == null) {
notifyPreloader("Requesting New Credentials");
getNewCredentials();
}
notifyPreloader("Reading remote manifest");
readRemoteManifest();
notifyPreloader("Reading local manifest");
readLocalManifest();
notifyPreloader("Comparing manifests");
compareManifests();
}
private void getNewCredentials() {
Platform.runLater(new Runnable() {
public void run() {
// Login window for new credentials
//Save credentials
}
});
}
private void readRemoteManifest() {
// REST API call to read remote manifest
if(HttpStatus.SC_UNAUTHORIZED) { // Stored credentials might have been read the first time which are expired, so try again.
getNewCredentials();
readRemoteManifest();
} else {
// update remote manifest object
}
}
private void readLocalManifest() {
// Update local manifest object
}
private void showAlert() {
Platform.runLater( new Runnable() {
// Display Alert window
});
}
private void compareManifests() {
if(remoteManifest == null) {
showAlert();
}
if(localAlert == null) {
showAlert();
}
if(remoteBuildNumber != localBuildNumber) {
showAlert();
}
}
#Override
public void start(Stage primaryStage){
//UI to select options and run tests
}
}
I have three issues with this approach.
If I don't user Platform.runLater() for Alert Windows and Login Windows, then I get an error saying Not on FX application thread; currentThread = JavaFX Application Thread error?
If I use Platform.runLater() then even though the login window is active, the remaining alert windows also show up immediately without waiting to read the login credentials.
If I manually create the file to store current credentials, then any alert window that I have shows up twice.
Let me know if you need any additional info.
Thanks in advance.
EDIT-1:
Someone gave me an answer to fix the first two issues. But now the answer is missing. If they can re-post the answer, it would be helpful for others.
The solution was to move the Platform.runLater() code to compareManifests() method from showAlert() method. Instead I moved the Platform.runLater() to init() method. Now I only have one call to runLater().
The modified code to fix the first two issues.
public class UI extends Application
{
String username=null, password=null;
Manifest remoteManifest, localManifest;
#Override
public void init() {
Platform.runLater( new Runnable() {
public void run() {
notifyPreloader("Loading Stored Credentials");
loadStoredCredentials();
if(username == null && password == null) {
notifyPreloader("Requesting New Credentials");
getNewCredentials();
}
notifyPreloader("Reading remote manifest");
readRemoteManifest();
notifyPreloader("Reading local manifest");
readLocalManifest();
notifyPreloader("Comparing manifests");
compareManifests();
}
});
}
private void getNewCredentials() {
// Login window for new credentials
//Save credentials
}
private void readRemoteManifest() {
// REST API call to read remote manifest
if(HttpStatus.SC_UNAUTHORIZED) { // Stored credentials might have been read the first time which are expired, so try again.
getNewCredentials();
readRemoteManifest();
} else {
// update remote manifest object
}
}
private void readLocalManifest() {
// Update local manifest object
}
private void showAlert() {
// Display Alert window
}
private void compareManifests() {
if(remoteManifest == null) {
showAlert();
}
if(localAlert == null) {
showAlert();
}
if(remoteBuildNumber != localBuildNumber) {
showAlert();
}
}
private void loadStoredCredentials() {
// Read the credentials from a local file
}
#Override
public void start(Stage primaryStage){
//UI to select options and run tests
}
}
Issue 3 is still occurring, there are two login windows being displayed now.
Thanks.

Android WebView evaluateJavaScript Callback is not being invoked

evaluateJavascript(String script, ValueCallback<String> resultCallback) method is added to WebView on Android in SDK 19.
Android documentation quotes:
If non-null, resultCallback will be invoked with any
result returned from that execution.
I am using this method as shown below, but somehow my callback is not being invoked. I can see from debugging that the evaluateJavascript() is called, but the call back is not being invoked in Android API 19, 20 & 21. From API 22 (LOLLIPOP_MR1) onwards, everything is working as expected.
Calling webview.loadURL("") before evaluateJavascript() makes it work on all the API levels. I want to understand why and would appreciate if somebody can shed some light / share any links about this. If I can understand why, I want to see if calling loadURL() could be avoided. There is another unrelated problem which makes loadURL() a non-preferable solution.
Code:
private void webViewTest() {
WebView webview = new WebView(this);
webview.getSettings().setJavaScriptEnabled(true);
Log.d("TEST", "BEFORE"); // LOGGED
// webview.loadUrl(""); // Enabling this makes it work on all Android versions
webview.evaluateJavascript("(function(){return 'test'})()", new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
Log.d("TEST", "From JS: " + s); // NEVER LOGGED on API 19-21
}
});
Log.e("TEST", "AFTER"); // LOGGED
}
runOnUiThread(new Runnable() {
#Override
public void run() {
webViewTest();
}
});
Please find an example of this problem at https://github.com/bashok001/TestApp
From the docs:
Asynchronously evaluates JavaScript in the context of the currently displayed page. In ContentViewCore EvaluateJavascript checks for renderview. Looks like for that particular versions of WebView integrated within 19,20,21 - the RenderView is just not created until loadUrl get called.
I found following FIX that could be relevant:
It's modify WebContentsAndorid native implementation of EvaluateJavaScript from :
void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env,
jobject obj,
jstring script,
jobject callback,
jboolean start_renderer) {
RenderViewHost* rvh = web_contents_->GetRenderViewHost();
DCHECK(rvh);
if (start_renderer && !rvh->IsRenderViewLive()) {
if (!static_cast<WebContentsImpl*>(web_contents_)->
CreateRenderViewForInitialEmptyDocument()) {
...
}
}
...
to :
void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env,
jobject obj,
jstring script,
jobject callback) {
RenderViewHost* rvh = web_contents_->GetRenderViewHost();
DCHECK(rvh);
if (!rvh->IsRenderViewLive()) {
if (!static_cast<WebContentsImpl*>(web_contents_)->
CreateRenderViewForInitialEmptyDocument()) {
...
}
}
....
And in ContentViewCore there was following code which passing false as start_renderer:
public void evaluateJavaScript(String script, JavaScriptCallback callback) {
assert mWebContents != null;
mWebContents.evaluateJavaScript(script, callback, false);
}
This mean that on WebView built prior to mentioned fix calls to evaluateJavaScript does not create RenderView and as result WebContext can't handle java script execution.
So when you use loadUrl you force creation of render view and all starts working as expected.

Android WebView getFavicon() returning null

I'm trying to get the favicon of the loaded page after using
WebView webView = new WebView(getActivity());
webView.loadUrl("http://" + url);
I'm attaching the asynchronous WebViewClient to the WebView to get the favicon after it loads
webView.setWebViewClient(new WebViewClient()
{
#Override
public void onPageFinished(WebView view, String url)
{
String linkTitle = view.getTitle();
Bitmap favicon = view.getFavicon();
onLinkUrlFinished(url, linkTitle);
}
});
The favicon getting back is always null, even for websites such as google/facebook that has favicons for sure.
Another thread says to use WebIconDatabase but it's deprecated:
Display the Android WebView's favicon
The API on android site refers to WebViewClient.onReceivedIcon which doesnt even exist.http://developer.android.com/reference/android/webkit/WebView.html#getFavicon%28%29
What's going on here?
In order to use onReceiveIcon(), you should use setWebChromeClient.
This is what I do and it's working for me.
webView.setWebChromeClient(new WebChromeClient() {
#Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
progressBar.setProgress(newProgress);
}
#Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
webImage.setImageBitmap(icon);
}
});
WebIconDatabase is deprecated as of API 19. According to the comments in the code:
#deprecated This class is only required when running on devices up to
{#link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
So unless you don't want to support API 18 and below, you should still be using WebIconDatabase:
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
And then, regardless what API you want to support, you need to specify in a custom WebChromeClient:
public class MyCustomWebChromeClient extends WebChromeClient {
#Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
// do whatever with the arguments passed in
}
}
Remember to register your custom WebChromeClient with your WebView:
mWebView.setWebChromeClient(new MyCustomWebChromeClient());
The key is to open the WebIconDatabase so WebView has somewhere to put the icons, and override WebChromeClient.onReceivedIcon. For additional information, see this StackOverflow article.
I know its an old thread but, for those facing problems getting favicon using webview client.
Kotlin:
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
tabTitle.text = view?.title // read website title
loadImg(view) // method to load the favicon
}
private fun loadImg (view: WebView?){
// u can directly use tabImage.setBitmap instead of assigning tabImg as val
val tabImg: ImageView = findViewById(R.id.tabImage)
// creating handler object to delay the associated thread a little bit after onPageFinished is called.
val handler = Handler()
val runnable = Runnable {
if(view?.favicon != null) {
tabImg.setImageResource(0) //remove the default image
tabImg.setImageBitmap(view?.favicon) // set the favicon
}
}
handler.postDelayed(runnable, 200) // delay time 200 ms
}
It worked for me, hope it helps new readers, plz up vote if it helps u, so that u can help others!
Best regards
So in the end I didn't end up using the deprecated API, instead I found out that if you put /favicon.ico after the domain, it'll give you the ico file, which I used in the end to fetch the image. The Uri API will have a getHost() method that will give you the host without having to manually parse it
String faviconUrl = Uri.parse(url).getHost() + "/favicon.ico";
For google for example the icon url will be www.google.com/favicon.ico

Can't find "this" class NoClassDefFoundError - Android

I'm trying very simply to test the 3.0 Facebook get started guide. https://developers.facebook.com/docs/getting-started/facebook-sdk-for-android/3.0/
I have had some problems with imports and references but I don't know it that's relevant. The issue I'm having is when I try to run the test Activity ant get this error:
Could not find class 'com.test1.test2.FacebookLogin$1', referenced from method com.test1.test2.FacebookLogin.onCreate
Code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_facebook_login);
//Error occurs here when I use 'this'
Session.openActiveSession(this, true, new Session.StatusCallback() {
// callback when session changes state
#Override
public void call(Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
// make request to the /me API
Request.executeMeRequestAsync(session, new Request.GraphUserCallback() {
// callback after Graph API response with user object
#Override
public void onCompleted(GraphUser user, Response response) {
if (user != null) {
TextView welcome = (TextView) findViewById(R.id.welcome);
welcome.setText("Hello " + user.getName() + "!");
}
}
});
}
}
});
}
How can I get a NoClassDefFoundError when I use this?
UPDATE:
After testing with the Scrumtious tutorial https://developers.facebook.com/docs/tutorials/androidsdk/3.0/scrumptious/authenticate/ as well I'm getting the same error when I call the Session.StatusCallback() method. I still don't know what my problem is though.
Thanks for the help
Maybe you updated to Android sdk tools revision 22. There is an issue there. You have to add the libraries you use in the export path. Check:
Android Sdk tools Revision 22 issue?

Categories

Resources