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 theExtend
class is present, then theBase
class will include it. - Instantiate the
Extend
class: If theExtend
class exists, then it is instantiated and assigned to the property$this->extend
which is defined inWisnet\BehatPom\Base
.
include
Example of /*
* 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 {
instantiate
Example of /*
* 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)
addToElements
function?
What is the purpose of the 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
initXPath
Using 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.
initXPath
implementation
Review an 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.
Base
functions
Overriding 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();
}
}