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

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'");
}
}

Related

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.

Behat - Check list element contains content

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?

Selenium 1 - switch to iframe without ID

The page i crape have removed the ID from their iframe, so I have a problem switching to the iframe, and I can't find any documentation to help me, so maybe there is someone here on Stack?
The url of the page is: http://www.klappen.se/boka/onlinebokning/
I'm using Selenium 1 and my code looks like this:
$this->_driver->switchTo()->getFrameByName("mainframe");
In my TargetLocator.php i have these functions:
<?php
// Copyright 2012-present Nearsoft, Inc
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace SeleniumClient;
require_once 'WebDriver.php';
class TargetLocator
{
private $_driver;
public function __construct(WebDriver $driver)
{
$this->_driver = $driver;
}
#region TargetLocator members
/**
* Move to a different frame using its index
* #param Integer $frameIndex
* #return current WebDriver
*/
public function getFrameByIndex($frameIndex)
{
$this->_driver->getFrame($frameIndex);
return $this->_driver;
}
/**
* Move to different frame using its name
* #param String $frameName
* #return current WebDriver
*/
public function getFrameByName($frameName)
{
//We should validate that frameName is string
/*
if ($frameName == null)
{
throw new ArgumentNullException("frameName", "Frame name cannot be null");
}
*/
$this->_driver->getFrame($frameName);
return $this->_driver;
}
/**
* Move to a frame element.
* #param WebElement $frameElement
* #return current WebDriver
*/
public function getFrameByWebElement(WebElement $frameElement)
{
//We should validate that frameElement is string
/*
if (frameElement == null)
{
throw new ArgumentNullException("frameElement", "Frame element cannot be null");
}
RemoteWebElement convertedElement = frameElement as RemoteWebElement;
if (convertedElement == null)
{
throw new ArgumentException("frameElement cannot be converted to RemoteWebElement", "frameElement");
}
*/
$frameId = $frameElement->getElementId();
$target = array('ELEMENT' => $frameId);
$this->_driver->getFrame($target);
return $this->_driver;
}
/**
* Change to the Window by passing in the name
* #param String $windowName
* #return current WebDriver
*/
public function getWindow($windowName)
{
$this->_driver->getWindow($windowName);
return $this->_driver;
}
/**
* Change the active frame to the default
* #return current WebDriver
*/
public function getDefaultFrame()
{
$this->_driver->getFrame(null);
return $this->_driver;
}
/**
* Finds the active element on the page and returns it
* #return WebElement
*/
public function getActiveElement()
{
$webElement = null;
$webElement = $this->_driver->getActiveElement();
return $webElement;
}
/**
* Switches to the currently active modal dialog for this particular driver instance.
* #return \SeleniumClient\Alert
*/
public function getAlert()
{
// N.B. We only execute the GetAlertText command to be able to throw
// a NoAlertPresentException if there is no alert found.
//$this->_driver->getAlertText();
return new Alert($this->_driver); //validate that the Alert object can be created, if not throw an exception, try to use a factory singleton o depency of injection to only use 1 instance
}
#endregion
}
I have tried them all, but can't get it to work. Is there anybody out there who can help:-)?
Thanks in advance.
As far as i see you are looking for this iFrame:
<iframe src="http://dlbookit3.dlsystems.se/dlbookitKSR/bmlogifilt/logifilt.aspx" style="height:700px; width:100%; border:0;"></iframe>
right? So you have a method like getFrameByWebElement(WebElement) which accepts a WebElement. I think you can use an xpath to find the webElement e.g.:
WebElement element = find(By.xpath("//iframe"));
getFrameByWebElement(element);
so far in theory this could work (this is Java, you have to adapt it for your php code). But if I analyze the HTML code of the page with chrome I cannot locate the webElement by using the xpath //iframe.
Still you can try... but it looks like that the page owner doesn't want its iFrame to be locateable anymore :-)
There is only 1 IFRAME on the page so you can just find it by tag name. The page loaded very slowly for me but I'm in the US so that may have something to do with it. You may have to wait for the IFRAME to become available and then get a handle to it.

Understanding cakephp3 error handling

I want to create a maintenance Page for my cake website by checking a Database Table for a maintenance flag using a sub-function of my AppController "initilize()" method. If the flag is set, i throw my custom MaintenanceException(Currently containing nothing special):
class MaintenanceException extends Exception{
}
To handle it, I implemented a custom App Exception Renderer:
class AppExceptionRenderer extends ExceptionRenderer {
public function maintenance($error)
{
return "MAINTENANCE";
}
}
I am able to see this maintenance Text on my website if I set my DB flag to true, but I could not find any information in cake's error handling documentation (http://book.cakephp.org/3.0/en/development/errors.html) on how I can actually tell the Exception renderer to render view "maintenance" with Template "infopage".
Can I even us that function using the ExceptionRenderer without a custom error controller? And If not, how should a proper ErrorController implementation look like? I already tried this:
class AppExceptionRenderer extends ExceptionRenderer {
protected function _getController(){
return new ErrorController();
}
public function maintenance($error)
{
return $this->_getController()->maintenanceAction();
}
}
together with:
class ErrorController extends Controller {
public function __construct($request = null, $response = null) {
parent::__construct($request, $response);
if (count(Router::extensions()) &&
!isset($this->RequestHandler)
) {
$this->loadComponent('RequestHandler');
}
$eventManager = $this->eventManager();
if (isset($this->Auth)) {
$eventManager->detach($this->Auth);
}
if (isset($this->Security)) {
$eventManager->detach($this->Security);
}
$this->viewPath = 'Error';
}
public function maintenanceAction(){
return $this->render('maintenance','infopage');
}
}
But this only throws NullPointerExceptions and a fatal error. I am really dissapointed by the cake manual as well, because the code examples there are nowhere close to give me an impression of how anything could be done and what functionality I actually have.
Because I had some more time today, I spent an hour digging into the cake Source and found a solution that works well for me (and is propably the way it should be done, altough the cake documentation does not really give a hint):
Step 1: Override the _template(...)-Method of the ExceptionRenderer in your own class. In my case, I copied the Method of the parent and added the following Code at the beginning of the method:
$isMaintenanceException = $exception instanceof MaintenanceException;
if($isMaintenanceException){
$template = 'maintenance';
return $this->template = $template;
}
This tells our Renderer, that the error Template called "maintentance"(which should be located in Folder: /Error) is the Error Page content it should render.
Step 2: The only thing we have to do now (And its is kinda hacky in my opinion, but proposed by the cake documentation in this exact way) is to set the layout param in our template to the name of the base layout we want to render with. So just add the following code on top of your error template:
$this->layout = "infopage";
The error controller I created is actually not even needed with this approach, and I still don't know how the cake error controller actually works. maybe I will dig into this if I have more time, but for the moment.

Best way to find the type of the object locator by passing the object locator alone in Selenium Webdriver

Is their any way to find the object locator type, by passing the object locator alone.
for e.g. i need to click on a login button, where its id=login, classname=loginbutton or xpath=//input[#name='login']. I need to build method where i will be just passing the objectlocator (either id or name) as the input and its type(either id or name) should be decided in the method like if it contains // then type should be of xpath etc.
I need to pass the objectLocator() which returns type to the findElement()
WebElement element = driver.findElement(objectLocator());
I do not think it is available off the shelf, you would have to implement your own logic.
The only thing is, let's say you want to search by linktext. As per your usecase, you would, in your object repo specify, "this is my linktext".
Now how do you know it is an id or a name or a linktext?
For xpath you can check if it starts with /, then its an xpath. If its only id or name then you can use ByIdorName, but i think it would become tricky with css and linktext.
The one thing I can think is you can establish some sort of conventions like if it is linktext precede your lcoator definition with linktext=blah blah and then you split and consume it.
I find it very useful to store all my locators as By objects and either use the By directly or pass the By into methods as I need them. For example:
By passwordField= By.id("login");
By userNameField = By.name("username");
By submitButton = By.xpath("\\myxpath\div[2]");
public void clickLogin() {
driver.findElement(submitButton).click();
}
I also use static Bys from other classes as well:
public void clickLogin() {
driver.findElement(LoginPage.SUBMIT_BUTTON).click();
}
The modern way to do this is using PageFactory and PageObjects
The following is a quick and dirty which will adapt selenium locators strings to WebDriver locators.
public enum LocatorType {
CLASSNAME, CSS, ID, LINK, NAME, TAGNAME, XPATH ;
}
public WebElement objectLocator(LocatorType type, String ref) {
switch(type) {
case ID:
return this.webDriver.findElement(By.id(ref));
case CLASSNAME:
return this.webDriver.findElement(By.className(ref));
case XPATH:
return this.webDriver.findElement(By.xpath(ref));
case CSS:
return this.webDriver.findElement(By.cssSelector(ref));
case LINK:
return this.webDriver.findElement(By.linkText(ref));
case NAME:
return this.webDriver.findElement(By.name(ref));
case TAGNAME:
return this.webDriver.findElement(By.tagName(ref));
}
return null;
}
public WebElement objectLocator(String identifier) {
String typeString = identifier.substring(0, identifier.indexOf('='));
String ref = identifier.substring(identifier.indexOf('=')+1, identifier.length());
if (typeString.toLowerCase().contains("classname")) {
return objectLocator(LocatorType.CLASSNAME, ref);
} else if (typeString.toLowerCase().contains("css")) {
return objectLocator(LocatorType.CSS, ref);
} else if (typeString.toLowerCase().contains("id")) {
return objectLocator(LocatorType.ID, ref);
} else if (typeString.toLowerCase().contains("link")) {
return objectLocator(LocatorType.LINK, ref);
} else if (typeString.toLowerCase().contains("name")) {
return objectLocator(LocatorType.NAME, ref);
} else if (typeString.toLowerCase().contains("tagname")) {
return objectLocator(LocatorType.TAGNAME, ref);
} else if (typeString.toLowerCase().contains("xpath")) {
return objectLocator(LocatorType.XPATH, ref);
} else {
return null;
}
}
It looks like you are looking for this solution because you have an object repository maintained somewhere outside of your code in some kind of properties file or xml.
Using gui maps has lot of disadvantages like,
- maintain an external file with a list of locators
- parse locator files to read keys (you can abstract this but still an overhead)
- when writing PageObjects you need to switch back and forth from Page to gui map
- possibility of multiple duplicate locators in gui maps
- object repo grows over time and becomes impossible to maintain
- debugging is far more difficult
What you are looking for is adding one more layer of complexity which is not required in my opinion. Automating browsers is a challenge in itself and writing maintainable test automation code is utmost important.
Use PageFactory in your page objects.
- Natural place for your locators are Page Objects themselves.
- Locators easily accessible in page objects for review or correction
- No need for explicit driver.findElement, with #FindBy you get that for free
- modern Java and awesome annotations make page objects look beautiful & readable
I have used gui maps before and struggled a lot. Switching to page factory made me realize that using object repository was such a bad idea!
This should do for locating element. I have given example till 3 level deep.
public WebElement findElement(String locator){
WebElement w = null;
try{
return (driver.findElement(By.id(locator)));
}catch(Exception e1){
try{
return ( driver.findElement(By.name(locator)));
}catch(Exception e2){
try{
return (driver.findElement(By.xpath(locator)));
}catch(Exception e3){
System.out.println("Cound not find a locator");
e3.printStackTrace();
}
}
}
return(w);
}
public void type(String locator, String value){
try{
WebElement w= findElement(locator);
w.sendKeys(""+value);
Thread.sleep(sleepTime);
}catch(Exception e){
e.printStackTrace();
}
}
-Vinay