Behat - Check list element contains content - selenium

I have a drop down menu containing list elements that are sometimes static and sometimes changed. My main goal is to check that the menu contains some content [followed by outputting that content and exporting it in a report].
The function I created in my FeatureContext.php looks like this:
/**
* #Then /^I check content exists for element "([^"]*)"$/
*/
public function iCheckElementContent($locator)
{
//check element exists on page
$element=$this->assertSession()->elementExists('css', $locator);
//check element content is not empty (returns exception if true)
if ( empty($this->getPage()->find('css', $locator)->getText()) ) {
throw new Exception;
}
}
As you can notice, it is based on the reply to the other question regarding this feature. My problem however is that it doesn't seem to like the getPage() parameter. The error I get is:
PHP Fatal error: Uncaught Error: Call to undefined method FeatureContext::getPage()
I also tried changing it to getValue(), without any success. Any ideas? (bonus awesome points for also helping me with the second step of my requirements)

I think I found a solution, but I'm not sure if it passes because it works or because it's searching for nothing and finding it.
$session = $this->getSession();
$element = $session->getPage()->find('css', $locator);
//check element content is not empty (returns exception if true)
if (empty ($element->getText()) ) {
throw new Exception;
}
Can someone please code-review this?

Related

Use specific status code error page in area | Asp.net core

I manage status codes error using
app.UseStatusCodePagesWithRedirects("/StatusCodeError/{0}");
But when an error occurs in the area, the page is redirected out of the area and shows the general error page.
How do I make sure that an area-specific error page is displayed if an error occurs in the area?
There is no extensibility point to modify the behavior of app.UseStatusCodePagesWithRedirects, the location is formatted internally using string.Format and with only one argument passed in the placeholder {0} for the status code. So at the calling time (passing in the location format), we have no understanding about the area which is a route value available only in the context of a request processing. So you can refer to the source for StatusCodePagesExtensions and can see that UseStatusCodePagesWithRedirects just depends on an overload of the extension method StatusCodePagesExtensions.UseStatusCodePages, you can freely copy the code and modify it to make another version of UseStatusCodePagesWithRedirects that accepts a location format which supports some arguments from the route values.
Here I've made a simple one for you:
public static class StatusCodePagesApplicationBuilderExtensions
{
public static IApplicationBuilder UseStatusCodePagesWithRouteDataAndRedirects(this IApplicationBuilder app, string locationFormat)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
var shouldPrependPathBase = locationFormat.StartsWith("~");
locationFormat = shouldPrependPathBase ? locationFormat.Substring(1) : locationFormat;
return app.UseStatusCodePages(context =>
{
//here is the point we can evaluate for the actual location
//by replacing the placeholders with route value
var location = Regex.Replace(locationFormat, #"\{[^}]+\}", m => {
//ignore the placeholder for status code
if (m.Value == "{0}") return m.Value;
var routeKey = m.Value.Trim('{', '}');
var routeValue = context.HttpContext.GetRouteValue(routeKey);
return routeValue?.ToString();
});
location = string.Format(CultureInfo.InvariantCulture, location, context.HttpContext.Response.StatusCode);
location = (shouldPrependPathBase ? context.HttpContext.Request.PathBase : PathString.Empty) + location;
context.HttpContext.Response.Redirect(location);
return Task.CompletedTask;
});
}
}
Now you can include any route data in the location format, in your case you just need the area, so the code will be like this:
app.UseStatusCodePagesWithRouteDataAndRedirects("/{area}/StatusCodeError/{0}");
NOTE: be careful with redirecting, you may encounter an error saying something like too many redirects, that's because of redirecting to a URL which in return causes a loop of redirects.

How to read hidden attributes from a page loaded in SWT Browser

I am working on an Eclipse plugin which loads a URL in the SWT browser. This page rendered in the browser has hidden html attributes. The requirement is to read the values of the hidden attributes.
Browser browser = new Browser(shell, SWT.NONE);
browser.setUrl("www.<my_url>.com");
I tried to execute a query on the DOM using the statusTextListener
browser.addStatusTextListener(new StatusTextListener() {
public void changed(StatusTextEvent event) {
browser.setData("query", event.text);
}
});
browser.addProgressListener(new ProgressListener() {
public void completed(ProgressEvent event) {
boolean result = browser
.execute("window.status=document.getElementById('main').childNodes[0].nodeValue;");
if (!result) {
/* Script may fail or may not be supported on certain platforms. */
System.out.println("Script was not executed.");
return;
}
String value = (String) browser.getData("query");
System.out.println("Node value: " + value);
}
});
However this does not seem to work. It works well if I try to load HTML text in the browser instead of the URL.
Any idea how to read DOM elements from the SWT browser after the page load is complete?
Use Browser::evaluate to execute Javascript in the context of the document and return the result to the caller.
To obtain the value of the first child of the main element in your example, start like this:
String script = "<Javascript to return an array of hidden attribute names>";
Object result = browser.evaluate(script);
The supported result types, however, are limited to string, number, and boolean - and arrays of these types. Javascript that evaluates to null or undefined will return null.
Hence, you will need to adjust your Javascript that queries the DOM to return a supported type.

How to assert page/tab/window title in Behat + Mink

I need to assert a page title for my test, which is the tab/window title using Behat+Mink
I tried getWindowName() but realized that is not the function I am looking for.
You should use a regular find by css for the title tag and use getText() to get the title.
The css should be: "head title"
Your solution is almost ok, you need to watch for possible exception, especially fatal ones that can stop your suite if encountered.
For example find() method will return an object or null, if null is returned and you are using getText() on it it will result in a fatal exception and your suite will stop.
Slightly improved method:
/**
* #Given /^the page title should be "([^"]*)"$/
*/
public function thePageTitleShouldBe($expectedTitle)
{
$titleElement = $this->getSession()->getPage()->find('css', 'head title');
if ($titleElement === null) {
throw new Exception('Page title element was not found!');
} else {
$title = $titleElement->getText();
if ($expectedTitle !== $title) {
throw new Exception("Incorrect title! Expected:$expectedTitle | Actual:$title ");
}
}
}
Improvements:
handled possible fatal exception
throw exception if element not found
throw exception with details if titles do not match
Note that you can also use other methods to check the title like: stripos, strpos or simply compare strings like i did. I prefer a simple compare if i need exact text or strpos/stripos method of php and I personally, avoid regular exceptions and associated methods like preg_match which are usually a bit slower.
One major improvement you could do is to have a method for waiting the element and handle the exception for you and use that instead of simple find, find you can use when you need to take decision based on the presence of the element like: if element exists do this else..
Thanks Lauda. Yes, that indeed worked. Wrote the function below:
/**
* #Given /^the page title should be "([^"]*)"$/
*/
public function thePageTitleShouldBe($arg1)
{
$actTitle = $this->getSession()->getPage()->find('css','head title')->getText();
if (!preg_match($arg1, $actTitle)) {
throw new Exception ('Incorrect title');
}
}
This didn't work for me in cases where the title is manipulated using Javascript and history.pushState/replaceState
Here an implementation that works for Javascript:
/**
* #Then /^the title is "([^"]*)"$/
*/
public function theTitleIs($arg1) {
$title = $this->getSession()->evaluateScript("return document.title");
if ($arg1 !== $title) {
throw new \Exception("expected title '$arg1', got '$title'");
}
}

[org.openqa.selenium.remote.RemoteWebElement#f76d0bdd -> unknown locator]

I am trying to read the element so that i can later use to get the id of that element. Using below code to get first the WebElement. But throws the following in console:
"[org.openqa.selenium.remote.RemoteWebElement#f76d0bdd -> unknown locator]"
WebElement ele = driver.switchTo().activeElement();
System.out.println("webelement is :"+ele);
You are seeing this because you are asking the code to essentially print ele.toString().
Which, according to the source, is going to give the exact message you see:
https://code.google.com/p/selenium/source/browse/java/client/src/org/openqa/selenium/remote/RemoteWebElement.java#375
Specifically:
public String toString() {
if (foundBy == null) {
return String.format("[%s -> unknown locator]", super.toString());
}
return String.format("[%s]", foundBy);
}
It says 'unknown locator' because one isn't explicitly set by setFoundBy.
So, I would suggest that if you want the ID of the element, you use:
ele.getAttribute("id");

Linqpad - Outputting into anchor to use title

I have a db that stores exception messages.
I would like to create a query that gets these exceptions but instead of dumping huge amounts of text i would prefer it to be "on demand".
I figured putting the exception into an anchor tag like so and then reading the message when needed by mousing over it would work... apparently not.
var logsForErrors = (from error in Logs
select new {
error = LINQPad.Util.RawHtml("<a title='"+ error.Exception+"'></a>"),
errorDate = error.Date,
errorMessage = error.Message
}).Take(10);
logsForErrors.Dump();
This is throwing an exception (lol) - "Cannot parse custom HTML: "
Encoding the exception message
...RawHtml("<a title='"+ Uri.EscapeDataString(error.Exception)+"'></a>")
Message Could not translate expression 'RawHtml((("h__TransparentIdentifier0.error.Exception)) +
"'>"))' into SQL and could not treat it as a local expression.
will generate a new error
Any ideas? - I am open to alternative solutions to this also.
I just want a container for the message instead of it just dumping right into the output as it it so huge!.
Thanks,
Kohan
Have you tried using the "Results to DataGrids" mode in the recent betas? It might do just what you need without having to write anything else.
Edit: your error was probably due to emitting HTML without escaping the text. The easiest solution is to call Util.RawHtml with an XElement instead of a string. You could write an extension method that does what you want like this:
public static class Extensions
{
public static object Tooltipize (this string data)
{
if (string.IsNullOrEmpty (data) || data.Length < 20) return data;
return Util.RawHtml (new XElement ("span", new XAttribute ("title", data), data.Substring (0, 20)));
}
}
Put this into My Extensions and you can use it from any query.