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;
}
Related
The javascript tries to fetch data from my spring boot api and gives back a "Failed to fetch" error everytime.
I'm sure the request reaches the api because for every click on submit I get the print statement, that i put in my get method, logged as it should. So something on the way back must be wrong.
Get method:
#RestController
#RequestMapping("/familyMember")
public class FamilyController {
private FamilyRepository familyRepository;
public FamilyController(FamilyRepository familyRepository) {
this.familyRepository = familyRepository;
}
#GetMapping("/id/{id}")
public FamilyMember getById(#PathVariable("id") Long id) {
Optional<FamilyMember> optional = familyRepository.findById(id);
if (!optional.isPresent()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
FamilyMember familyMember = optional.get();
System.out.println(id); //print statement to make sure api is reached
return familyMember;
}
Javascript code:
const url = 'http://localhost:4001/familyMember/';
submit.onclick = async() => {
const endpoint = url + 'id/' + input.value;
try {
const response = await fetch(endpoint); // error occures here because no code below is executed
output.style.color = 'green'; // to see if fetch continues(apparently it doesn't)
if (response.ok) {
output.innerHTML += await response.json();
}
} catch(error) {
output.innerHTML += error; // gives me "Failed to fetch" in the html
}
I'm not sure if the bug is on the server side or client side. The api gives me the correct information when I use curl in the terminal...so propably the js code?
Thanks in advance.
Actually found the answer. Above #RestController I need the annotation #CrossOrigin sothat the api allows requests from different origins than itself (in that case a html file).
#CrossOrigin
#RestController
#RequestMapping("/familyMember")
public class FamilyController {
...
You should specify cross origin with an adress like "localhost:4001"
As mentioned in this document http://wopi.readthedocs.io/en/latest/discovery.html, I was wondering if there is a way to use the Action URL Dynamically?
What do you mean by "dynamically"?
When you load the discovery file, you can dynamically build action URLs by replacing placeholders such as <ui=UI_LLCC&>.
Here's my C# code which should be easily transformable to Java:
public async Task<string> GetFileUrlAsync(string extension, string wopiFileUrl, WopiActionEnum action, WopiUrlSettings urlSettings = null)
{
var combinedUrlSettings = new WopiUrlSettings(urlSettings.Merge(UrlSettings));
var template = await WopiDiscoverer.GetUrlTemplateAsync(extension, action);
if (!string.IsNullOrEmpty(template))
{
// Resolve optional parameters
var url = Regex.Replace(template, #"<(?<name>\w*)=(?<value>\w*)&*>", m => ResolveOptionalParameter(m.Groups["name"].Value, m.Groups["value"].Value, combinedUrlSettings));
url = url.TrimEnd('&');
// Append mandatory parameters
url += "&WOPISrc=" + Uri.EscapeDataString(wopiFileUrl);
return url;
}
return null;
}
Note that the WopiUrlBuilder uses WopiDiscoverer that facilitates low level operations upon the discovery file.
I am using Titanium (Appcelerator) to connect to Fitbit API. (http://www.appcelerator.com)
I have been facing issues of getting "Invalid Signature" when I am trying to request for token.
I'm using HTTPClient from Titanium.Network.HTTPClient class to send the HTTP Request.
I also uses the oauth-1.0a.js library from https://github.com/ddo/oauth-1.0a to assist in getting the nonce and signature value.
Here is the code:
Ti.include('/oauth/ddo/hmac-sha1.js');
Ti.include('/oauth/ddo/enc-base64-min.js');
Ti.include('/oauth/ddo/oauth-1.0a.js');
function FitBitAuth() {
FitBitAuth.signatureMethod = "HMAC-SHA1";
FitBitAuth.clientKey = 'XXXXXXXXXXXXXXXXXXXXXXXXX';
FitBitAuth.clientSecret = 'XXXXXXXXXXXXXXXXXXXXXXXXXX';
FitBitAuth.nonce = "R#nD0m_$tR!nGss";
FitBitAuth.request_token_url = "https://api.fitbit.com/oauth/request_token";
FitBitAuth.callback_url = "http://www.fitbit.com";
}
FitBitAuth.prototype.createConsumerTokenSecretPair = function() {
return OAuth({
consumer : {
public : FitBitAuth.clientKey,
secret : FitBitAuth.clientSecret
},
signature_method : FitBitAuth.signatureMethod
});
};
FitBitAuth.prototype.getRequestTokenRequestData = function() {
return {
url : "https://api.fitbit.com/oauth/request_token",
method : 'POST'
};
};
FitBitAuth.prototype.requestToken = function() {
var oauth = this.createConsumerTokenSecretPair();
var request_data = this.getRequestTokenRequestData();
var authorized_request = oauth.authorize(request_data, '', FitBitAuth.nonce, FitBitAuth.timestamp);
//alert(authorized_request);
return authorized_request;
};
function auth1a() {
var fb = new FitBitAuth();
var rt = fb.requestToken();
var req = Ti.Network.createHTTPClient();
req.open("POST", FitBitAuth.request_token_url);
req.setRequestHeader('Authorization', 'OAuth oauth_consumer_key="'+FitBitAuth.clientKey+'"');
Ti.API.info(rt);
req.send({
oauth_timestamp : rt.oauth_timestamp,
oauth_nonce : rt.oauth_nonce,
oauth_signature : encodeURIComponent(rt.oauth_signature),
oauth_signature_method: rt.oauth_signature_method,
oauth_callback : encodeURIComponent(FitBitAuth.callback_url),
oauth_version : rt.oauth_version
});
req.onload = function() {
var json = this.responseText;
Ti.API.info("HEADER =====================");
Ti.API.info(req.getAllResponseHeaders());
Ti.API.info("END HEADER =================");
Ti.API.info(json);
var response = JSON.parse(json);
//alert(response);
};
}
I have also tried the Fitbit API Debug tool to assist me in getting all the signature right, in fact the signature and base String do match with the one shown by Fitbit API Debug Tool.
However, I keep getting this Invalid Signature, a sample JSON return is shown below:
{"errors":[{"errorType":"oauth","fieldName":"oauth_signature","message":"Invalid signature: rN**ahem**SGJmFwHp6C38%2F3rMKEe6ZM%3D"}],"success":false}
I have also already tested to do the curl way and it works from Terminal, but to no avail it does not give me a success from Titanium.
Any help is appreciated.
I manage to solve it.
I tried to use another way of inserting the parameters through the header.
Such that the setRequestHeader will look like this:
req.setRequestHeader('Authorization', 'OAuth oauth_consumer_key="'+FitBitAuth.clientKey+'", oauth_nonce="'+rt.oauth_nonce+'", oauth_signature="'+rt.oauth_signature+'",...');
Alternatively, we can also use the built in toHeader feature of the oauth library that I'm using:
oauth.toHeader(oauth_data);
The code above will produce the oauth data in key-value pair.
{
'Authorization' : 'OAuth oauth_consumer_key="xxxxxxxxxxxxx"&oauth_nonce="xxxxxx"&...
}
So instead of the long code for setRequestHeader, we can make use of the value of toHeader, code shown below:
req.setRequestHeader('Authorization', oauth.toHeader(oauth_data).Authorization);
Do note that the return result by fitbit is in plaintext.
auth_token=xxxxxxxx&auth_token_secret=xxxxxxxxx&...
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
I am developing an extension in which whatever request is coming i want to change the request parameters and send a dummy request to server to get response.
for eg. if original request loaded is www.abc.com/eg.php?user="some code", then i want to change it into www.abc.com/eg.php?user="ritubala" and get html page in the response for further processing inside my extension..
i used these codes given in following url
Return HTML content as a string, given URL. Javascript Function
but it is causing recursion...
so in short i want to get html code from a url inside my extension...plz do help
Use nsITraceableChannel, note that you get a copy of the content being loaded.
If you abort the request then it aborts giving you a copy.
Here's me using nsITraceableChannel: https://github.com/Noitidart/demo-nsITraceableChannel
What I think you want is this:
const { Ci, Cu, Cc, Cr } = require('chrome'); //const {interfaces: Ci, utils: Cu, classes: Cc, results: Cr } = Components;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/devtools/Console.jsm');
var observers = {
'http-on-examine-response': {
observe: function (aSubject, aTopic, aData) {
console.info('http-on-modify-request: aSubject = ' + aSubject + ' | aTopic = ' + aTopic + ' | aData = ' + aData);
var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
var requestUrl = httpChannel.URI.spec
if (requestUrl.indexOf('blah')) {
//httpChannel.cancel(Cr.NS_BINDING_ABORTED); //this is how you abort but if use redirectTo you don't need to abort the request
httpChannel.redirectTo(Services.io.newURI('http://www.anotherwebsite/', null, null));
}
},
reg: function () {
Services.obs.addObserver(observers['http-on-modify-request'], 'http-on-modify-request', false);
},
unreg: function () {
Services.obs.removeObserver(observers['http-on-modify-request'], 'http-on-modify-request');
}
}
};
//register all observers
for (var o in observers) {
observers[o].reg();
}
note the redirectTo function is for FF20+
https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIHttpChannel#redirectTo%28%29
so combine this example with the nsITraceableChannel demo and you'll have what you want.
if you don't want to redirect you can abort the request, get the DOCUMENT of the request and then xmlhttprequest the new page which gives you the source FIRST and doesnt load it to tab, and then modify the source then load it back to the DOCUMENT.
OR instead of xmlhttprequesting it, just abort the request, redirectTo the new page and then modify the tab. to get the tab of the channel see solution here:
Security Error when trying to load content from resource in a Firefox Addon (SDK)
also to do http requests from an extension see this snippet: https://gist.github.com/Noitidart/9088113