Some of requests to my Web Application return data not in HTML format (JSON).
How to handle this correctly?
I wrote the following page definition:
import com.fasterxml.jackson.databind.ObjectMapper
import geb.Page
class JsonResponse extends Page {
static url = null;
static at = {
true;
}
static ObjectMapper mapper = new ObjectMapper();
static content = {
readTree {
def jsonString = $("pre").text();
mapper.readTree(jsonString)
}
}
}
and it apparently works. But the question is, how correct it is?
It takes data from inside pre tag. This is because I saw this content inside driver.pageSource. Is this correct? May be it is driver-dependent?
I put null into url, since the page has different url depending on query. Is this correct?
Geb is not intended to be used to interact with HTTP API endpoints because it is built on top of WebDriver and hence expects to be used via a browser and on HTML pages.
If you want to test HTTP API endpoints then I would suggest using an http client to back your tests. There are many of them out in the wild, just to name a few in no particular order:
Apache HttpClient
RestAssured - this one is specifically for testing
Ratpack's TestHttpClient - again intended to be used for testing
OkHttp
Unirest
I was able to download the content of a PDF in a geb unit test using the Direct Download API. It is handy because it takes all the cookies from the session but does the download separately from the browser.
Example from that documentation:
Browser.drive {
go "http://myapp.com/login"
// login
username = "me"
password = "secret"
login().click()
// now find the pdf download link
def downloadLink = $("a.pdf-download-link")
// now get the pdf bytes
def bytes = downloadBytes(downloadLink.#href)
}
There are different methods for downloading different kinds of data. See the DownloadSupport API docs.
Because geb uses HttpsURLConnection to connect to an https endpoint instead of using the browser you can have problems with self-signed certificates. I solved that using this Stack Overflow answer.
I agree that Geb is not intended to be used to interact with HTTP API endpoints, but there are some contexts where this can be helpful, so add this snippet here for posterity:
when: 'I retrieve the JSON transaction list'
go '/transaction/transactionList'
then: 'Valid data is retrieved'
JsonSlurper jsonSlurper = new JsonSlurper()
Map<String, List> transactionList = jsonSlurper.parseText(driver.pageSource)
assert transactionList.categories.class == ArrayList
Related
I have an AspNet/AngularJS website (using System.Web.Http version 5.2.6.0), I'm adding card payment integration, with 3DS security. 3DS has this step called "data device gathering" where the PSP endpoint sends you back an url and you have to post it into a hidden iframe on the frontend.
Presumably there's some javascript that gathers the device data, and then redirects the iframe to an url you have provided. It almost doesn't matter what page is behind that url, the important thing is listening for the redirect. It just needs to contain some specific html so you can verify you were redirected to the correct page.
So I just added a static page to my website and provided the url. The redirect attempt resulted in "405 method not allowed" because the PSP's device gathering logic does a POST to the redirect url.
So I'm trying to make an API method that allows POST and returns a html page. This has proved unexpectedly challenging. These are some of the things I've tried from answers here on StackOverflow:
[HttpPost]
[AllowAnonymous]
[Route("methodnotification")]
public NegotiatedContentResult<string> MethodNotification()
{
return base.Content(System.Net.HttpStatusCode.OK,"<div id = \"threeDSMethodData\" name = \"threeDSMethodData\" > PROCESSING...</ div >");
}
This will return the following in PostMan:
"<div id = "threeDSMethodData" name = "threeDSMethodData" >
PROCESSING...</ div >"
It may look OK, but it's actually a string of type application/json; charset=utf-8. I tried the POST from FireFox, it returns this:
< string> <div id = "threeDSMethodData" name = "threeDSMethodData"
> PROCESSING...</ div > < /string>
Even setting the Accept header to only accept "text/html" will return a "plain text" not html.
I've tried different overloads of base.Content, which required me to add a "formatter" (if I also wanted to specify content type), and change the return type of the method to FormattedContentResult. After experimenting with different formatters I gave up because it just keeps giving me JSON or string.
I've also tried the following:
return new System.Web.Mvc.ContentResult
{
Content = "<div id = \"threeDSMethodData\" name = \"threeDSMethodData\" > PROCESSING...</ div >",
ContentType = "text/html"
};
Note the fully qualified name was necessary here because if I add a using statement for System.Web.Mvc, all my annotations start giving me "ambiguous reference" between System.Web.Mvc and System.Web.Http. This approach also returned a JSON file.
How do I return html from a POST in ASPNET?
EDIT: The API project targets .NET Framework 4.6. I wish I knew which version of ASPNET that corresponded to.
Try to change the function definition from
public NegotiatedContentResult<string> MethodNotification()
to
public ContentResult MethodNotification()
You should use NegotiatedContentResult if you are passing an object to be made text/json i.e:
NegotiatedContentResult<MyDTOClass>
This is why you receive the json string.
By spefifying NegotiatedContentResult<STRING> you break the text/html contentType, cause string implies, json string
The Blazor app in Visual Studio uses a Http.GetFromJsonAsync call to get the data for Weather Forecasts from a json file in wwwroot.
When I change the data in the file, I still see the same data in the table?
When I copy the file, and change the code to use the new filename, I get the changed results.
Is there some caching happening with wwwroot files? I've tried hard refresh, that doesn't make a difference, but changing browser does. I know that Blazor caches the framework files...but is this happening to all wwwroot, how do I change this behaviour?
Thanks in advance.
The fetchdata sample page (from new blazorwasm) retrieves data on initialize component:
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}
When you go out of this page and come back, initialize is running again and a request is done.
But, because this is a GET request, the browser can deliver answer from cache:
They are some ways to avoid cache on Blazor GET requests, learn about it here: Bypass HTTP browser cache when using HttpClient in Blazor WebAssembly
Also, you can use the simple trick to add a random string to query string:
protected override async Task OnInitializedAsync()
{
var randomid = Guid.NewGuid().ToString();
var url_get = $"sample-data/weather.json?{randomid}";
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>(url_get);
}
In short, it seems to get cached because a get request can be cached by browser and is the browser who retrieve the data.
I'm working on an App in Angular 6. When the app is deployed, it exchanges data with an online database. When I'm working on the app, however, I would like it to exchange data with a local database. I have a single service setup to do all of the communication, so when I am working on the app I can simply change the base URL, but I was wondering if there was a way I could just redirect that service in development, and then have it go to the database as normal in the production environment.
I am aware I can add a proxy.conf.json file, which I'm already doing to access a separate online API, so I dont know if it is as simple as just adding another element to that json file.
I haven't posted any sample code as this is more of a general question as to methodology in angular versus a specific line of code question, but I'm happy to post whatever people would like to see for clarification.
You can do one thing,
you can configure your url in both environment.ts and environment.prod.ts like
environment = {
...
url: 'something.com/api',
...
}
and use it like
import { environment } from 'environment/environment';
const url = environment.url;
this will give you different url for normal build (Development env.) and prod build (Deploy env.)
Also, don't worry about using just environmet.ts in import statement, as when you create prod build your environment.ts gets content from environment.prod.ts. so you will be using url from environment.prod.ts
In Angular you have access to a function isDevMode() which determines if the app is currently in devmode.
Based on that, you should be able to adjust your base url as needed.
import { isDevMode } from '#angular/core';
private baseUrl: string = '';
const devUrl: string = '...';
const prodUrl: string = '...';
if (isDevMode()) {
this.baseUrl = devUrl
}
else {
this.baseUrl = prodUrl
};
I'm new to API testing. I need to integrate only one flow of API through Selenium as rest of GUI is already present.
I use API url on client browser, I get the result[json format]
I tried on SoapUI [REST] with the url, it gave the result[json format]
But when I try it on Selenium, I'm getting internal server error 500.
Please let me know the config changes that I need to take care.
string apiurl = "https://example.org/alfresco/s/org/alfresco/faceted/search?"
Here is the snippet of code i tried
URL url = new URL(apiurl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
String authString = "user:Password";
byte[] bytesEncoded = Base64.encodeBase64(authString.getBytes());
String authEncoded = new String("Basic "+bytesEncoded);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn.setRequestProperty("Authorization", authEncoded);
conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent", "Apache-HttpClient/4.1.1 (java 1.5)");
conn.connect();
System.out.println(conn.getResponseCode());
I split the URL as
URL url = new URL(url);
conn.setRequestProperty("resource", "/alfresco/s/org/alfresco/faceted/search?");
conn.setRequestProperty("query", "%28Keywords%3A%27test%27%29");
conn.setRequestProperty("format", "json");
conn.setRequestProperty("resource", url+ "alfresco/s/org/alfresco/faceted/search?query=%28Keywords%3A%27test%27%29&count=10&format=json");
conn.setRequestProperty("Accept", "application/json");
Here I get 200 as response, but the response is on the example.org and not on the query appended.
Kindly let me know what is wrong that have done or need to add any information.
Maybe it is a little rude and not regarding current topic but I want to prevent you from doing a wrong job in future.
Using Selenium Webdriver for API testing you'll suffer a lot of pain.
Selenium Webdriver is not used for this porpuse.
Instead it there are a lot of good automation tools to test API.
One of them is Postman (chrome extension https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en)
Using this tool you'll get much more features and will be able to test any API with any requirenments.
I doubt you'll be able to do it with selenium Webdriver.
For some reason, any document I upload to OneNote via the new REST API is corrupt when viewed from OneNote. Everything else is fine, but the file (for example a Word document) isn't clickable and if you try and open is shows as corrupt.
This is similar to what may happen when there is a problem with the byte array, or its in memory, but that doesn't seem to be the case. I use essentially the same process to upload the file bytes to SharePoint, OneDrive, etc. It's only to OneNote that the file seems to be corrupt.
Here is a simplified version of the C#
HttpRequestMessage createMessage = null;
HttpResponseMessage response = null;
using (var streamContent = new ByteArrayContent(fileBytes))
{
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
streamContent.Headers.ContentDisposition.Name = fileName;
createMessage = new HttpRequestMessage(HttpMethod.Post, authorizationUrl)
{
Content = new MultipartFormDataContent
{
{
new StringContent(simpleHtml,
System.Text.Encoding.UTF8, "text/html"), "Presentation"
},
{streamContent}
}
};
response = await client.SendAsync(createMessage);
var stream = await response.Content.ReadAsStreamAsync();
successful = response.IsSuccessStatusCode;
}
Does anyone have any thoughts or working code uploading an actual binary document via the OneNote API via a Windows Store app?
The WinStore code sample contains a working example (method: CreatePageWithAttachedFile) of how to upload an attachment.
The slight differences I can think of between the above code snippet and the code sample are that the code sample uploads a pdf file (instead of a document) and the sample uses StreamContent (while the above code snippet uses ByteArrayContent).
I downloaded the code sample and locally modified it to use a document file and ByteArrayContent. I was able to upload the attachment and view it successfully. Used the following to get a byte array from a given stream:
using (BinaryReader br = new BinaryReader(stream))
{
byte[] b = br.ReadBytes(Convert.ToInt32(s.Length));
}
The rest of the code looks pretty similar to the above snippet and overall worked successfully for me.
Here are a few more things to consider while troubleshooting the issue:
Verify the attachment file itself isn't corrupt in the first place. for e.g. can it be opened without the OneNote API being in the mix?
Verify the API returned a 201 Http Status code back and the resulting page contains the attachment icon and allows downloading/viewing the attached file.
So, the issue was (strangely) the addition of the meta Content Type in the tag sent over in the HTML content that's not shown. The documentation refers to adding a type=[mime type] in the object tag, and since the WinStore example didn't do this (it only adds the mime type to the MediaTypeHeaderValue I removed it and it worked perfectly.
Just changing it to this worked:
<object data-attachment=\"" + fileName + "\" data=\"name:" + attachmentPartName + "\" />
Thanks for pointing me in the right direction with the sample code!