Capturing console.log within a UIWebView - javascript

I'm writing an iOS application with MonoTouch that does some javascript interaction with a UIWebView. For debugging purposes, it would be nice to be able to "capture" console.log in the javascript that runs in the UIWebView together with the rest of the application output. Is this possible? Examples using regular Objective-C code is also OK.

After some more googling, I came about this answer: Javascript console.log() in an iOS UIWebView
Converting it to MonoTouch yields this solution:
using System;
using System.Web;
using System.Json;
using MonoTouch.UIKit;
namespace View
{
public class JsBridgeWebView : UIWebView
{
public object BridgeDelegate {get;set;}
private const string BRIDGE_JS = #"
function invokeNative(functionName, args) {
var iframe = document.createElement('IFRAME');
iframe.setAttribute('src', 'jsbridge://' + functionName + '#' + JSON.stringify(args));
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
var console = {
log: function(msg) {
invokeNative('Log', [msg]);
}
};
";
public JsBridgeWebView ()
{
ShouldStartLoad += LoadHandler;
LoadFinished += (sender, e) => {
EvaluateJavascript(BRIDGE_JS);
};
}
public bool LoadHandler (UIWebView webView, MonoTouch.Foundation.NSUrlRequest request, UIWebViewNavigationType navigationType)
{
var url = request.Url;
if(url.Scheme.Equals("jsbridge")) {
var func = url.Host;
if(func.Equals("Log")) {
// console.log
var args = JsonObject.Parse(HttpUtility.UrlDecode(url.Fragment));
var msg = (string)args[0];
Console.WriteLine(msg);
return false;
}
return true;
}
}
}
}
Now all console.log statements in javascript in a UIWebView will be sent to Console.WriteLine. This could of course be extended to any kind of output one would want.

Can you add javascript code that does something like this to overwrite the method:
console.log = function(var text) {
consoleforios += text;
}
Then from the web view, call:
string console = webView.EvaluatingJavaScript("return consoleforios;");
This might not be something I'd leave in permanently, but it should work.

Related

Capture REST calls with Selenium

I run integration test with Selenium as a test runner and webdriver.io javascript library for Selenium API.
My test goes as follows:
I load an html page and click on a button. I want to check if a Get REST call was invoked.
I found a plugin for webdriver.io called webdriverajax that intend to fit to my requirements but it just doesn't work.
Any ideas how do capture rest calls?
You can achieve this by using custom HttpClient class that is out side from selenium code.As far as i know selenium doesn't support this feature.
Assume when you clicked the button it will called a REST service , the URL can be grab from the HTML DOM element.Then you can use your custom code to verify if URL is accessible or not.Then you can decide if your test is pass or failed based on the status code or some other your mechanism.
FileDownloader.java(Sample code snippet)
private String downloader(WebElement element, String attribute) throws IOException, NullPointerException, URISyntaxException {
String fileToDownloadLocation = element.getAttribute(attribute);
if (fileToDownloadLocation.trim().equals("")) throw new NullPointerException("The element you have specified does not link to anything!");
URL fileToDownload = new URL(fileToDownloadLocation);
File downloadedFile = new File(this.localDownloadPath + fileToDownload.getFile().replaceFirst("/|\\\\", ""));
if (downloadedFile.canWrite() == false) downloadedFile.setWritable(true);
HttpClient client = new DefaultHttpClient();
BasicHttpContext localContext = new BasicHttpContext();
LOG.info("Mimic WebDriver cookie state: " + this.mimicWebDriverCookieState);
if (this.mimicWebDriverCookieState) {
localContext.setAttribute(ClientContext.COOKIE_STORE, mimicCookieState(this.driver.manage().getCookies()));
}
HttpGet httpget = new HttpGet(fileToDownload.toURI());
HttpParams httpRequestParameters = httpget.getParams();
httpRequestParameters.setParameter(ClientPNames.HANDLE_REDIRECTS, this.followRedirects);
httpget.setParams(httpRequestParameters);
LOG.info("Sending GET request for: " + httpget.getURI());
HttpResponse response = client.execute(httpget, localContext);
this.httpStatusOfLastDownloadAttempt = response.getStatusLine().getStatusCode();
LOG.info("HTTP GET request status: " + this.httpStatusOfLastDownloadAttempt);
LOG.info("Downloading file: " + downloadedFile.getName());
FileUtils.copyInputStreamToFile(response.getEntity().getContent(), downloadedFile);
response.getEntity().getContent().close();
String downloadedFileAbsolutePath = downloadedFile.getAbsolutePath();
LOG.info("File downloaded to '" + downloadedFileAbsolutePath + "'");
return downloadedFileAbsolutePath;
}
TestClass.java
#Test
public void downloadAFile() throws Exception {
FileDownloader downloadTestFile = new FileDownloader(driver);
driver.get("http://www.localhost.com/downloadTest.html");
WebElement downloadLink = driver.findElement(By.id("fileToDownload"));
String downloadedFileAbsoluteLocation = downloadTestFile.downloadFile(downloadLink);
assertThat(new File(downloadedFileAbsoluteLocation).exists(), is(equalTo(true)));
assertThat(downloadTestFile.getHTTPStatusOfLastDownloadAttempt(), is(equalTo(200)));
// you can use status code to valid the REST URL
}
Here is the reference.
Note: This may not exactly fit into your requirement but you can get some idea and modify it accordingly to fit into your requirement.
Also refer the BrowserMob Proxy using this you can also achieve what you want.
The problem was the webdriver.io version. Apparently, webdriverajax works fine just with webdriver.io v3.x but not with v4.x. I use v4.5.2.
I decide not using a plugin and implement a mock for window.XMLHttpRequest
open and send methods, as follows:
proxyXHR() {
this.browser.execute(() => {
const namespace = '__scriptTests';
window[namespace] = { open: [], send: [] };
const originalOpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (...args) {
window[namespace].open.push({
method: args[0],
url: args[1],
async: args[2],
user: args[3],
password: args[4]
});
originalOpen.apply(this, [].slice.call(args));
};
window.XMLHttpRequest.prototype.send = function (...args) {
window[namespace].send.push(JSON.parse(args[0]));
};
});
}
getXHRsInfo() {
const result = this.browser.execute(() => {
const namespace = '__scriptTests';
return window[namespace];
});
return result.value;
}

I cannot execute ShellExecute from javascript to run a Windows Form with parameters

is it really possible to run an application installed on a users machine with parameters using an activex control. I can successfully run the application in this form:
$('#ejecutarActivo').click(function () {
var Comando = $('#Comando').val();
if (Comando.trim() != "") {
try {
var objShell = new window.ActiveXObject("shell.application");
objShell.ShellExecute('C:\\Users\\Jerry\\Documents\\visual studio 2012\\Projects\\PruebaComandos\\PruebaComandos\\bin\\Debug\\PruebaComandos.exe', '', '', 'open',1);
}
catch (e) {
alert('Active opciones de seguridad y utilice el navegador Internet Explorer');
}
}
});
However I don't seem to be able to run it with parameters. I really need to run it that way and I'm able to run it with the parameters from the command line, but I've tried almost every combination of single and double quotes, and also simply putting the variable name (e.g. "Comando") in the invocation but everytime I put something inside the parameter section, (which according to Microsoft documentation MSDN is the second parameter to ShellExecute) the application starts in my computer but nothing is displayed on the screen (I mean, I see the application is running in the Task Manager but something goes wrong and nothing displays).
I need to check also the application to see if something is going wrong in the windows form. But I would like to know what's wrong in the way I'm invoking the activex.
Can anybody help?
I found the answer, it wasn't a problem with ShellExecute javascript method. Actually, oops!, it was a problem with my Windows Form, I guess I will better gain a better understanding of Serial Ports before I ask.
I left the javascript like so:
$('#ejecutarActivo').click(function () {
var Comando = $('#Comando').val();
if (Comando.trim() != "") {
try {
var objShell = new window.ActiveXObject("shell.application");
if (Comando.indexOf('"') != -1) {
Comando = Comando.replace(/"+/g, '"""');
}
objShell.ShellExecute('C:\\Users\\Jerry\\Documents\\visual studio 2012\\Projects\\PruebaComandos\\PruebaComandos\\bin\\Debug\\PruebaComandos.exe', Comando , '', 'open', 1);
}
catch (e) {
alert('Active opciones de seguridad y utilice el navegador Internet Explorer');
}
}
});
And the code of the Windows Form goes like so: (It was a problem related to the port not closing before another invocation, which crashed the application. It's surprisingly simple)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
namespace PruebaComandos
{
public partial class Form1 : Form
{
SerialPort Puerto = new SerialPort();
string buffer = String.Empty;
Thread DemoThread;
byte[] Existen;
public Form1(string argumento)
{
InitializeComponent();
Puerto.PortName = "Com4";
Puerto.BaudRate = 9600;
this.textoComandoLinea.Text = argumento;
Puerto.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
CloseSerial();
OpenSerial();
Puerto.Write(argumento + "\r\n");
}
public Form1()
{
InitializeComponent();
Puerto.PortName = "Com4";
Puerto.BaudRate = 9600;
Puerto.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
CloseSerial();
OpenSerial();
Puerto.Write("Inicio");
}
private void botonSend_Click(object sender, EventArgs e)
{
string Input = textoComandoLinea.Text;
OpenSerial();
Puerto.Write(Input);
}
void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
switch (e.EventType)
{
case SerialData.Chars:
buffer += sp.ReadExisting();
this.DemoThread = new Thread(new ThreadStart(ThreadProcSafe));
this.DemoThread.Start();
break;
case SerialData.Eof:
CloseSerial();
break;
}
}
void OpenSerial()
{
if (!Puerto.IsOpen)
{
Puerto.Open();
}
}
void CloseSerial()
{
if (Puerto.IsOpen)
{
Puerto.Close();
}
}
private void ThreadProcSafe()
{
this.SetText(buffer);
//CloseSerial();
}
delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (this.textoRecibido.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.BeginInvoke(d, new object[] { text });
}
else
{
if (text.EndsWith("\r\n"))
{
this.textoRecibido.Text = text;
if (MessageBox.Show(this, text, "", MessageBoxButtons.YesNo, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0x40000) == DialogResult.Yes)
{
Application.Exit();
}
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
}
}
Anyway, the question was dumb enough to be closed in a day, I only post my code in case it helps someone. Goodbye.

UWP WebView calls dynamic JavaScript

I have studied Window Universal App these days. When working with WebView, I see it can invoke a script inside the html file, but how about executing an external JavaScript script?
WebView webview = new WebView();
webview.Navigate(new Uri("https://www.google.com"));
string script = #"
function getBodyHTML()
{
var innerHTML = document.getElementsByTagName('body')[0].innerHTML;
return innerHTML;
}
";
I want to get the bodyHTML, possibly using something like this:
// pseudo code
webview.IncludeScript(script);
string bodyHTML = webview.ExecuteScript("getBodyHTML()");
oops, just realize that the similar question was answered.
I could do my thing by:
string[] arguments = new string[] {#"document.getElementsByTagName('body')[0].innerHTML;"};
try
{
string bodyHTML = await webview.InvokeScriptAsync("eval", arguments);
}
catch (Exception ex)
{
}

EO.WebBrowser set referer C#

Trying to find a way to set the referer in EO.WebBrowser.
I saw that the User-Agent can be changed with:webView1.CustomUserAgent but looks like there is no method for the referer. Are there any other ways to get this working in javascript or by other means?
I can "capture" the beforesendheaders event, with this: webView1.BeforeSendHeaders += new EO.WebBrowser.RequestEventHandler(webView1_BeforeSendHeaders);, but doesn't help that much.
I'm working on a project, and I started with awesomium, but... looks like some websites are not loaded, it's just showing a blank screen. I've managed to change both referer and user agent in awesomium, but I really need both to move on.
Any suggestions are appreciated.
Looks like I've found it myself, using javascript.
Here's the code:
public partial class Form1 : Form
{
private const string JS_referer_function = "function navigateToUrl(url) {var f = document.createElement(\"FORM\"); f.action = url; var indexQM = url.indexOf(\"?\"); if (indexQM>=0) { var params = url.substring(indexQM+1).split(\"&\"); for (var i=0; i<params.length; i++) { var keyValuePair = params[i].split(\"=\"); var input = document.createElement(\"INPUT\"); input.type=\"hidden\"; input.name = keyValuePair[0]; input.value = keyValuePair[1]; f.appendChild(input); } } document.body.appendChild(f); f.submit(); }";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
webView1.LoadUrlAndWait("http://referer.com");
webView1.EvalScript(JS_referer_function);
webView1.EvalScript("navigateToUrl(\"http://192.168.0.108/referer\");");
}
}
It's basically loading http://referer.com first, and using javascript, goes to http://192.168.0.108/referer using the referer http://referer.com.

Calling server side function from signalR in Android

SignalR connected in Android, But I want to call a function which is available on server,
I tried the following code,
String host = "Host URL";
HubConnection hubConnection = new HubConnection(host, "", false, new Logger() {
#Override
public void log(String message, LogLevel level) {
System.out.println("message - " + message);
}
});
hubProxy = hubConnection.createHubProxy("Task");
SignalRFuture<Void> signalRFuture = hubConnection.start().done(addSession("Session ID"));
And
private Action<Void> addSession(String sessionID) {
//hubProxy. [server is not available here]
}
In javascript, I tried like following,
$.connection.hub.start().done(function () {
addSession(sessionId)
});
function addSession(sessionId) {
proxy.server.addSession(sessionId)
.done(function () {
isHubConnected = true;
}
);
}
In javascript this works perfectly, But in android :( ,
Update
By trying like #AnikIslamAbhi's answer,
signalRFuture = hubConnection.start().done(addSession("sessionID"));
private Action<Void> addSession(String sessionID) {
System.out.println("addSession : " + sessionID);
hubProxy.invoke("addSession", sessionID);
return null;
}
I received following error message,
InvalidStateException: The operation is not allowed in the 'Connecting' state
In javascript you are using Auto proxy.
But in android you are using manual proxy
Both have differences in their behavior.
Like
To call serverside method using auto proxy
proxy.server.addSession(sessionId)
To call serverside method using manual proxy
proxy.invoke("addSession", sessionId)
You can find more on this link

Categories

Resources