QA No Err

QA No Err

  • Docs

›Tutorials

Overview of Project

  • Overview

Getting Started

  • Walk through example

Tutorials

  • behat.yml
  • Reset data
  • Extend class
  • Snippets
  • Partials
  • CBT usage

Understanding the Extend class

Use the Extend class to include additional functionality not provided by the Chrome Extension

How is the Extend class named?

The Extend classes all are prefixed w/ the Base class name that they are extending. For example, the AccountClient.php class was generated by the Chrome Extension. One of the required elements that a Gherkin test needs was not generated. So to add that element create a class name AccountClientExtend.php. The AccountClient was the base or root name.

How is the Extend class used?

When the QANoErr Chrome Extension is used to process a page, the emitted class includes checks for the existence of an Extend class. There are two checks,

  • Include the Extend class: If the Extend class is present, then the Base class will include it.
  • Instantiate the Extend class: If the Extend class exists, then it is instantiated and assigned to the property $this->extend which is defined in Wisnet\BehatPom\Base.

Example of include

/*
*  Extend class provides support for overriding all the functions
*/
if (file_exists('features/bootstrap/pages/AccountClientExtend.php')){
    include 'features/bootstrap/pages/AccountClientExtend.php';
}
class AccountClient extends Base {

Example of instantiate

   /*
   * Create variable if AccountClient exists
   */
    public function __construct($session, $factory, $parameters) {
        parent::__construct($session, $factory, $parameters);

        if (file_exists('features/bootstrap/pages/AccountClientExtend.php')) {
            $this->extend = new \pages\AccountClientExtend;
             $this->extend->addToElements($this);       
        }
    }

What is the bare minimum structure of the Extend class

The Extend class must:

  • Use the namespace pages
  • Follow the naming convention of <Root>Extend.php
  • Implement the one function public function addToElements($parent)

What is the purpose of the addToElements function?

The design of the QANoErr Page Object Models is to identify elements that are on the page being tested. Sometimes the Chrome Extension doesn't find the elements correctly or the elements weren't present. For example, there might be a Error message that only appears when the page is submitted. If the Chrome Extension was used when that Error message was not visible, then the generated class will not include that Error message element.

Using the addToElements function allows the tester to add additional elements for access by the Gherkin tests. There are two parts of this addToElements,

  • xpath selector
  • plain name

The xpath selector portion contains the xpath, for example:

   $xpath = array("xpath" => "//input[@name='mepr-account-form']");

The plain name is a readable term that the Gherkin code can reference. The below name Save Profile is associated to the xpath selector in the following statement:

        $parent->_addToElements("Save Profile", $xpath);

The entire function looks like this:

 public function addToElements($parent) {
        $xpath = array("xpath" => "//input[@name='mepr-account-form']");
        $parent->_addToElements("Save Profile", $xpath);

    }

How is the xpath selector used?

The xpath selector is used to identify elements. The Page Object Model classes that are generated by the QANoErr Chrome Extension and their associated Extend classes, are mainly used to identify elements on a page for testing. Using the xpath selector, the Wisnet\BehatPom\Base can access all these elements when processing the Gherkin code

Using initXPath

When working with multiple devices and supporting responsive websites, it easily happens that a Link or Button is manipulated to be part of a hamburger. For example, on Desktops, some elements will be visible but the same page on a small phone, the Link or Button may only be visible when the hamburger icon is clicked.

In this responsive scenario, the XPath selectors may change depending on the device. One way we decided to handle this was when Clicking a Link or Button, we would first provide an opportunity to change the XPath depending on the device.

Review an initXPath implementation

Two scenarios are supported, Click the button and Click the Link. In the FeatureContext, both implementations start w/ the call to the setUpXPath in the Base.

Here's the first few lines from iClickTheButton implementation. The only difference w/ iClickTheLink is the last statement, from clickButton to clickLink.

    public function iClickTheButton($arg1)  {
        try {
            //dynamic xpath?
            $this->current->setupXPath($arg1);
            //make sure it's visible
            $this->iWaitForTheElementToBeVisible($arg1);
            
            $this->current->clickButton($arg1);

The Base class implements the setupXPath as shown below. If the extend class exists, and the function initXPath exists, then that function is invoked.

    /*
     * Define XPath dynamically
     */
    public function setupXPath($linkname) {
        if (isset ($this->extend)
            &&
            method_exists($this->extend, 'initXPath')) {
            $this->extend->initXPath($this, $linkname);
        }   
    }

Let's look at an implementation in the ClientMenuExtend.php. This class handles the menu options for a Client as opposed to a Admin type user. When the menu is displayed on a small device, the hamburger menu will display the link when the hamburger is clicked.

We have multiple anchor tags then for the same text. For example, the link "My Account" appears 2 times in the page. The first time, when on a Desktop. The second, when the hamburger icon has been clicked.

Here is the default definition for the "My Account" option:

    public function addToElements($parent) {
        $xpath = array("xpath" => "(//a[text()='My Account'])[1]");
        $parent->_addToElements("My Account", $xpath);
        

So when the Gherkin step is like this: And I click the "My Account" link, we don't know what device we're actually running on and thus, we don't know which XPath selector to use.

Here's how this is implemented in the ClientMenuExtend.php:

    /**
     * Setup the XPath for elements that are similar but w/ different index
     */
    function initXPath($parent, $linkname) {
        if ($linkname == 'My Account') {
            $more = $parent->getElement('More');
            if ($more->isVisible()) {
                $xpath = array("xpath" => "(//a[text()='My Account'])[2]");
                $parent->_addToElements("My Account", $xpath);
            }

At run time, we check which $linkname is to be clicked. We check if the element More is visible, and if so, we update the XPath selector to use the 2nd occurance of the My Account link. This is done at run time.

Overriding Base functions

The Wisnet\BehatPom\Base class contains all the functions that support the Gherkin implementations. It does things like:

  • get a value from a field
  • fill a field with a value
  • click a button
  • click a link
  • etc

All the Base needs to know is what the XPath Selector is for the Page Object Model. But sometimes the page will have different menu options as described above with responsive devices and you don't know until run time and so you have to interagate the page.

For example, back with this ClientMenuExtend.php class, there are two Log out selectors. One for desktop and the other for mobiles. So when the Gherkin has the Log out step, we can override the clickLink and determine if we are in a responsive situation.

Here's an example. Note if the More element is visible, we use a different XPath selector. This could have been solved w/ the initXPath way too.

    public function clickLink($parent, $linkname) {
        if ($linkname == 'Log Out') {
            $more = $parent->getElement('More');
            if ($more->isVisible()) {            
                $more->click();
                sleep(1);
                $link = $parent->getElement('Mobile Log Out');
                $link->click();
            } else {
                $link = $parent->getElement($linkname);
                $link->click();
            }

Here's the standard way all the functions in the Base.php class work w/ the Extend class, if the Extend class is set and the method exists, then we call that Extend class method. Otherwise we just do the default behavior.

    public function clickLink($linkname) {
        if (isset ($this->extend)
            &&
            method_exists($this->extend, 'clickLink')) {
            $this->extend->clickLink($this, $linkname);
        }  else {
            $link = $this->getElement($linkname);
            $link->click();
        }   
    }
← PreviousNext →
  • How is the Extend class named?
  • How is the Extend class used?
  • Example of include
  • Example of instantiate
  • What is the bare minimum structure of the Extend class
  • What is the purpose of the addToElements function?
  • How is the xpath selector used?
  • Using initXPath
  • Review an initXPath implementation
  • Overriding Base functions
Copyright © 2018 wisnet.com